/*
* Copyright 2000-2014 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.tests.components.ui;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinSession;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.UI;
import com.vaadin.util.CurrentInstance;
public class UIAccess extends AbstractTestUIWithLog {
private volatile boolean checkCurrentInstancesBeforeResponse = false;
private Future<Void> checkFromBeforeClientResponse;
private class CurrentInstanceTestType {
private String value;
public CurrentInstanceTestType(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}
@Override
protected void setup(VaadinRequest request) {
addComponent(new Button("Access from UI thread",
new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.clear();
// Ensure beforeClientResponse is invoked
markAsDirty();
checkFromBeforeClientResponse = access(new Runnable() {
@Override
public void run() {
log("Access from UI thread is run");
}
});
log("Access from UI thread future is done? "
+ checkFromBeforeClientResponse.isDone());
}
}));
addComponent(new Button("Access from background thread",
new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.clear();
final CountDownLatch latch = new CountDownLatch(1);
new Thread() {
@Override
public void run() {
final boolean threadHasCurrentResponse = VaadinService
.getCurrentResponse() != null;
// session is locked by request thread at this
// point
final Future<Void> initialFuture = access(new Runnable() {
@Override
public void run() {
log("Initial background message");
log("Thread has current response? "
+ threadHasCurrentResponse);
}
});
// Let request thread continue
latch.countDown();
// Wait until thread can be locked
while (!getSession().getLockInstance()
.tryLock()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
try {
log("Thread got lock, inital future done? "
+ initialFuture.isDone());
setPollInterval(-1);
} finally {
getSession().unlock();
}
}
}.start();
// Wait for thread to do initialize before continuing
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
setPollInterval(3000);
}
}));
addComponent(new Button("Access throwing exception",
new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.clear();
final Future<Void> firstFuture = access(new Runnable() {
@Override
public void run() {
log("Throwing exception in access");
throw new RuntimeException(
"Catch me if you can");
}
});
access(new Runnable() {
@Override
public void run() {
log("firstFuture is done? "
+ firstFuture.isDone());
try {
firstFuture.get();
log("Should not get here");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
log("Got exception from firstFuture: "
+ e.getMessage());
}
}
});
}
}));
addComponent(new Button("Cancel future before started",
new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.clear();
Future<Void> future = access(new Runnable() {
@Override
public void run() {
log("Should not get here");
}
});
future.cancel(false);
log("future was cancelled, should not start");
}
}));
addComponent(new Button("Cancel running future",
new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.clear();
final ReentrantLock interruptLock = new ReentrantLock();
final Future<Void> future = access(new Runnable() {
@Override
public void run() {
log("Waiting for thread to start");
while (!interruptLock.isLocked()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
log("Premature interruption");
throw new RuntimeException(e);
}
}
log("Thread started, waiting for interruption");
try {
interruptLock.lockInterruptibly();
} catch (InterruptedException e) {
log("I was interrupted");
}
}
});
new Thread() {
@Override
public void run() {
interruptLock.lock();
// Wait until UI thread has started waiting for
// the lock
while (!interruptLock.hasQueuedThreads()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
future.cancel(true);
}
}.start();
}
}));
addComponent(new Button("CurrentInstance accessSynchronously values",
new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.clear();
// accessSynchronously should maintain values
CurrentInstance.set(CurrentInstanceTestType.class,
new CurrentInstanceTestType(
"Set before accessSynchronosly"));
accessSynchronously(new Runnable() {
@Override
public void run() {
log.log("accessSynchronously has request? "
+ (VaadinService.getCurrentRequest() != null));
log.log("Test value in accessSynchronously: "
+ CurrentInstance
.get(CurrentInstanceTestType.class));
CurrentInstance.set(
CurrentInstanceTestType.class,
new CurrentInstanceTestType(
"Set in accessSynchronosly"));
}
});
log.log("has request after accessSynchronously? "
+ (VaadinService.getCurrentRequest() != null));
log("Test value after accessSynchornously: "
+ CurrentInstance
.get(CurrentInstanceTestType.class));
}
}));
addComponent(new Button("CurrentInstance access values",
new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.clear();
// accessSynchronously should maintain values
CurrentInstance
.setInheritable(CurrentInstanceTestType.class,
new CurrentInstanceTestType(
"Set before access"));
access(new Runnable() {
@Override
public void run() {
log.log("access has request? "
+ (VaadinService.getCurrentRequest() != null));
log.log("Test value in access: "
+ CurrentInstance
.get(CurrentInstanceTestType.class));
CurrentInstance.setInheritable(
CurrentInstanceTestType.class,
new CurrentInstanceTestType(
"Set in access"));
}
});
CurrentInstance.setInheritable(
CurrentInstanceTestType.class,
new CurrentInstanceTestType(
"Set before run pending"));
getSession().getService().runPendingAccessTasks(
getSession());
log.log("has request after access? "
+ (VaadinService.getCurrentRequest() != null));
log("Test value after access: "
+ CurrentInstance
.get(CurrentInstanceTestType.class));
}
}));
addComponent(new Button("CurrentInstance when pushing",
new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
log.clear();
if (getPushConfiguration().getPushMode() != PushMode.AUTOMATIC) {
log("Can only test with automatic push enabled");
return;
}
final VaadinSession session = getSession();
new Thread() {
@Override
public void run() {
// Pretend this isn't a Vaadin thread
CurrentInstance.clearAll();
/*
* Get explicit lock to ensure the (implicit)
* push does not happen during normal request
* handling.
*/
session.lock();
try {
access(new Runnable() {
@Override
public void run() {
checkCurrentInstancesBeforeResponse = true;
// Trigger beforeClientResponse
markAsDirty();
}
});
} finally {
session.unlock();
}
}
}.start();
}
}));
}
@Override
public void beforeClientResponse(boolean initial) {
if (checkFromBeforeClientResponse != null) {
log("beforeClientResponse future is done? "
+ checkFromBeforeClientResponse.isDone());
checkFromBeforeClientResponse = null;
}
if (checkCurrentInstancesBeforeResponse) {
UI currentUI = UI.getCurrent();
VaadinSession currentSession = VaadinSession.getCurrent();
log("Current UI matches in beforeResponse? " + (currentUI == this));
log("Current session matches in beforeResponse? "
+ (currentSession == getSession()));
checkCurrentInstancesBeforeResponse = false;
}
}
@Override
protected String getTestDescription() {
return "Test for various ways of using UI.access";
}
@Override
protected Integer getTicketNumber() {
return Integer.valueOf(11897);
}
}