diff options
10 files changed, 287 insertions, 13 deletions
diff --git a/server/src/main/java/com/vaadin/server/ConnectorIdGenerationEvent.java b/server/src/main/java/com/vaadin/server/ConnectorIdGenerationEvent.java new file mode 100644 index 0000000000..5cfba15e97 --- /dev/null +++ b/server/src/main/java/com/vaadin/server/ConnectorIdGenerationEvent.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed 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 com.vaadin.server; + +import java.util.EventObject; + +import com.vaadin.shared.Connector; + +/** + * Event object containing information related to connector id generation. + * + * @author Vaadin Ltd + * @since 8.1 + */ +public class ConnectorIdGenerationEvent extends EventObject { + + private final VaadinSession session; + private final Connector connector; + + /** + * Creates a new event for the given session and connector. + * + * @param session + * the session for which a connector id is needed, not + * <code>null</code> + * @param connector + * the connector that should get an id, not <code>null</code> + */ + public ConnectorIdGenerationEvent(VaadinSession session, + Connector connector) { + super(session.getService()); + + assert session != null; + assert connector != null; + + this.session = session; + this.connector = connector; + } + + /** + * Gets the session for which connector id is needed. + * + * @return the session, not <code>null</code> + */ + public VaadinSession getSession() { + return session; + } + + /** + * Gets the connector that should get an id. + * + * @return the connector, not <code>null</code> + */ + public Connector getConnector() { + return connector; + } + + @Override + public VaadinService getSource() { + return (VaadinService) super.getSource(); + } +} diff --git a/server/src/main/java/com/vaadin/server/ConnectorIdGenerator.java b/server/src/main/java/com/vaadin/server/ConnectorIdGenerator.java new file mode 100644 index 0000000000..c40b9cfbd6 --- /dev/null +++ b/server/src/main/java/com/vaadin/server/ConnectorIdGenerator.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed 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 com.vaadin.server; + +import java.io.Serializable; + +/** + * Callback for generating the id for new connectors. A generator can be + * registered to be used with an application by overriding + * {@link VaadinService#initConenctorIdGenerator(java.util.List)} or by calling + * {@link ServiceInitEvent#addConnectorIdGenerator(ConnectorIdGenerator)} from a + * {@link VaadinServiceInitListener}. + * + * @author Vaadin Ltd + * @since 8.1 + */ +@FunctionalInterface +public interface ConnectorIdGenerator extends Serializable { + /** + * Generates a connector id for a connector. + * + * @param event + * the event object that has a reference to the connector and the + * session, not <code>null</code> + * @return the connector id to use for the connector, not <code>null</code> + */ + public String generateConnectorId(ConnectorIdGenerationEvent event); + + /** + * Generates a connector id using the default logic by using + * {@link VaadinSession#getNextConnectorId()}. + * + * @param event + * the event object that has a reference to the connector and the + * session, not <code>null</code> + * @return the connector id to use for the connector, not <code>null</code> + */ + public static String generateDefaultConnectorId( + ConnectorIdGenerationEvent event) { + assert event != null; + return event.getSession().getNextConnectorId(); + } +} diff --git a/server/src/main/java/com/vaadin/server/ServiceInitEvent.java b/server/src/main/java/com/vaadin/server/ServiceInitEvent.java index 482c23dd23..1bd5e1b78f 100644 --- a/server/src/main/java/com/vaadin/server/ServiceInitEvent.java +++ b/server/src/main/java/com/vaadin/server/ServiceInitEvent.java @@ -35,6 +35,7 @@ public class ServiceInitEvent extends EventObject { private List<RequestHandler> addedRequestHandlers = new ArrayList<>(); private List<DependencyFilter> addedDependencyFilters = new ArrayList<>(); + private List<ConnectorIdGenerator> addedConnectorIdGenerators = new ArrayList<>(); /** * Creates a new service init event for a given {@link VaadinService} and @@ -99,6 +100,41 @@ public class ServiceInitEvent extends EventObject { return Collections.unmodifiableList(addedDependencyFilters); } + /** + * Adds as connector id generator to be used by this service. By default, + * the service will fail to deploy if more than one connector id generator + * has been registered. + * + * @param connectorIdGenerator + * the connector id generator to add, not <code>null</code> + * + * @since 8.1 + */ + public void addConnectorIdGenerator( + ConnectorIdGenerator connectorIdGenerator) { + Objects.requireNonNull(connectorIdGenerator, + "Connector id generator cannot be null"); + + /* + * We're collecting all generators so that a custom service + * implementation can pick which one to use even though the default + * implementation throws if there are more than one. + */ + addedConnectorIdGenerators.add(connectorIdGenerator); + } + + /** + * Gets an unmodifiable list of all connector id generators that have been + * added for the service. + * + * @return the current list of added connector id generators + * + * @since 8.1 + */ + public List<ConnectorIdGenerator> getAddedConnectorIdGenerators() { + return Collections.unmodifiableList(addedConnectorIdGenerators); + } + @Override public VaadinService getSource() { return (VaadinService) super.getSource(); diff --git a/server/src/main/java/com/vaadin/server/VaadinService.java b/server/src/main/java/com/vaadin/server/VaadinService.java index 649826a5d6..65ef09f2a3 100644 --- a/server/src/main/java/com/vaadin/server/VaadinService.java +++ b/server/src/main/java/com/vaadin/server/VaadinService.java @@ -136,6 +136,7 @@ public abstract class VaadinService implements Serializable { private Iterable<RequestHandler> requestHandlers; private Iterable<DependencyFilter> dependencyFilters; + private ConnectorIdGenerator connectorIdGenerator; private boolean atmosphereAvailable = checkAtmosphereSupport(); @@ -209,6 +210,11 @@ public abstract class VaadinService implements Serializable { dependencyFilters = Collections.unmodifiableCollection( initDependencyFilters(event.getAddedDependencyFilters())); + + connectorIdGenerator = initConenctorIdGenerator( + event.getAddedConnectorIdGenerators()); + assert connectorIdGenerator != null; + initialized = true; } @@ -1477,6 +1483,49 @@ public abstract class VaadinService implements Serializable { } /** + * Determines the connector id generator to use for the application. + * <p> + * The connector id generator creates a unique id for each connector + * attached to a UI. + * <p> + * The framework collects generators from the {@link SessionInitEvent} where + * session init listeners can add them. This method is called with the + * combined list to determine one generator to use. + * <p> + * If the list is empty, a default implementation based on + * {@link VaadinSession#getNextConnectorId()} is used. If the list contains + * one item, it is used. If there are multiple generators in the list, an + * exception is thrown. + * + * @since 8.1 + * @param addedConnectorIdGenerators + * a list of connector id generators collected from the session + * init event, not <code>null</code> + * @return the connector id generator to use, not <code>null</code> + * + * @throws ServiceException + * if something went wrong while determining the filters, e.g. + * if there are multiple implementations to choose from + * + */ + protected ConnectorIdGenerator initConenctorIdGenerator( + List<ConnectorIdGenerator> addedConnectorIdGenerators) + throws ServiceException { + assert addedConnectorIdGenerators != null; + + switch (addedConnectorIdGenerators.size()) { + case 0: + return ConnectorIdGenerator::generateDefaultConnectorId; + case 1: + return addedConnectorIdGenerators.get(0); + default: + throw new ServiceException( + "Cannot start application since there are multiple connector id generators. Remove redundant implementations from the classpath or override VaadinService.initConenctorIdGenerator to explicitly select one to use. The found generators are: " + + addedConnectorIdGenerators); + } + } + + /** * Gets the filters which all resource dependencies are passed through * before being sent to the client for loading. * @@ -2149,4 +2198,32 @@ public abstract class VaadinService implements Serializable { return VaadinSession.class.getName() + "." + getServiceName(); } + /** + * Generates a unique id to use for a newly attached connector. + * + * @see ConnectorIdGenerator + * @see #initConenctorIdGenerator(List) + * + * @since 8.1 + * + * @param session + * the session to which the connector has been attached, not + * <code>null</code> + * @param connector + * the attached connector for which to generate an id, not + * <code>null</code> + * @return a string id that is unique within the session, not + * <code>null</code> + */ + public String generateConnectorId(VaadinSession session, + ClientConnector connector) { + assert session.getService() == this; + String connectorId = connectorIdGenerator.generateConnectorId( + new ConnectorIdGenerationEvent(session, connector)); + + assert connectorId != null; + + return connectorId; + } + } diff --git a/server/src/main/java/com/vaadin/server/VaadinSession.java b/server/src/main/java/com/vaadin/server/VaadinSession.java index de5c192846..fdc8a28b8b 100644 --- a/server/src/main/java/com/vaadin/server/VaadinSession.java +++ b/server/src/main/java/com/vaadin/server/VaadinSession.java @@ -754,11 +754,25 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * A connector that has not yet been assigned an id. * @return A new id for the connector * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version + * @deprecated As of 7.0. Use + * {@link VaadinService#generateConnectorId(VaadinSession, ClientConnector)} + * instead. */ @Deprecated public String createConnectorId(ClientConnector connector) { + return service.generateConnectorId(this, connector); + } + + /** + * Gets the next unused numerical id for connector ids. + * + * @since 8.1 + * + * @return the next unused numerical id for connector ids, not + * <code>null</code> + * + */ + public String getNextConnectorId() { assert hasLock(); return String.valueOf(connectorIdSequence++); } diff --git a/server/src/test/java/com/vaadin/server/MockVaadinSession.java b/server/src/test/java/com/vaadin/server/MockVaadinSession.java index 07854560b3..0dc5010b1a 100644 --- a/server/src/test/java/com/vaadin/server/MockVaadinSession.java +++ b/server/src/test/java/com/vaadin/server/MockVaadinSession.java @@ -64,6 +64,12 @@ public class MockVaadinSession extends VaadinSession { referenceKeeper.remove(); } + @Override + public String createConnectorId(ClientConnector connector) { + // Don't delegate to service which may be null or a broken mock + return getNextConnectorId(); + } + private int closeCount; private final ReentrantLock lock = new ReentrantLock(); diff --git a/server/src/test/java/com/vaadin/tests/server/component/abstractsinglecomponentcontainer/RemoveFromParentLockingTest.java b/server/src/test/java/com/vaadin/tests/server/component/abstractsinglecomponentcontainer/RemoveFromParentLockingTest.java index 2da2d6aede..f963152960 100644 --- a/server/src/test/java/com/vaadin/tests/server/component/abstractsinglecomponentcontainer/RemoveFromParentLockingTest.java +++ b/server/src/test/java/com/vaadin/tests/server/component/abstractsinglecomponentcontainer/RemoveFromParentLockingTest.java @@ -15,14 +15,12 @@ */ package com.vaadin.tests.server.component.abstractsinglecomponentcontainer; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import com.vaadin.server.MockVaadinSession; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinSession; import com.vaadin.ui.UI; @@ -37,14 +35,7 @@ public class RemoveFromParentLockingTest { } private static VerticalLayout createTestComponent() { - VaadinSession session = new VaadinSession(null) { - private final ReentrantLock lock = new ReentrantLock(); - - @Override - public Lock getLockInstance() { - return lock; - } - }; + VaadinSession session = new MockVaadinSession(null); session.getLockInstance().lock(); try { diff --git a/uitest/src/main/java/com/vaadin/tests/applicationservlet/ServiceInitListeners.java b/uitest/src/main/java/com/vaadin/tests/applicationservlet/ServiceInitListeners.java index 16e4609267..5c649a26b3 100644 --- a/uitest/src/main/java/com/vaadin/tests/applicationservlet/ServiceInitListeners.java +++ b/uitest/src/main/java/com/vaadin/tests/applicationservlet/ServiceInitListeners.java @@ -24,5 +24,7 @@ public class ServiceInitListeners extends AbstractTestUIWithLog { protected void setup(VaadinRequest request) { log("Init count: " + TestingServiceInitListener.getInitCount()); log("Request count: " + TestingServiceInitListener.getRequestCount()); + log("Connector id count: " + + TestingServiceInitListener.getConnectorIdCount()); } } diff --git a/uitest/src/main/java/com/vaadin/tests/applicationservlet/TestingServiceInitListener.java b/uitest/src/main/java/com/vaadin/tests/applicationservlet/TestingServiceInitListener.java index d0685ad975..d100108c3c 100644 --- a/uitest/src/main/java/com/vaadin/tests/applicationservlet/TestingServiceInitListener.java +++ b/uitest/src/main/java/com/vaadin/tests/applicationservlet/TestingServiceInitListener.java @@ -18,6 +18,8 @@ package com.vaadin.tests.applicationservlet; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; +import com.vaadin.server.ConnectorIdGenerationEvent; +import com.vaadin.server.ConnectorIdGenerator; import com.vaadin.server.RequestHandler; import com.vaadin.server.ServiceInitEvent; import com.vaadin.server.VaadinRequest; @@ -29,6 +31,7 @@ public class TestingServiceInitListener implements VaadinServiceInitListener { private static AtomicInteger initCount = new AtomicInteger(); private static AtomicInteger requestCount = new AtomicInteger(); + private static AtomicInteger connectorIdCount = new AtomicInteger(); @Override public void serviceInit(ServiceInitEvent event) { @@ -43,6 +46,15 @@ public class TestingServiceInitListener implements VaadinServiceInitListener { return false; } }); + + event.addConnectorIdGenerator(new ConnectorIdGenerator() { + @Override + public String generateConnectorId( + ConnectorIdGenerationEvent event) { + connectorIdCount.incrementAndGet(); + return ConnectorIdGenerator.generateDefaultConnectorId(event); + } + }); } public static int getInitCount() { @@ -53,4 +65,8 @@ public class TestingServiceInitListener implements VaadinServiceInitListener { return requestCount.get(); } + public static int getConnectorIdCount() { + return connectorIdCount.get(); + } + } diff --git a/uitest/src/test/java/com/vaadin/tests/applicationservlet/ServiceInitListenersTest.java b/uitest/src/test/java/com/vaadin/tests/applicationservlet/ServiceInitListenersTest.java index d97f668967..a23a6f1a1f 100644 --- a/uitest/src/test/java/com/vaadin/tests/applicationservlet/ServiceInitListenersTest.java +++ b/uitest/src/test/java/com/vaadin/tests/applicationservlet/ServiceInitListenersTest.java @@ -28,6 +28,7 @@ public class ServiceInitListenersTest extends SingleBrowserTest { Assert.assertNotEquals(getLogRow(0), 0, extractCount(getLogRow(0))); Assert.assertNotEquals(getLogRow(1), 0, extractCount(getLogRow(1))); + Assert.assertNotEquals(getLogRow(2), 0, extractCount(getLogRow(2))); } private int extractCount(String logRow) { |