@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); |
@@ -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; | |||
} | |||
@@ -1476,6 +1482,49 @@ public abstract class VaadinService implements Serializable { | |||
return sessionInitFilters; | |||
} | |||
/** | |||
* 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; | |||
} | |||
} |
@@ -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++); | |||
} |
@@ -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(); |
@@ -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 { |
@@ -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()); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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) { |