Last active
July 27, 2017 08:28
-
-
Save ldelaprade/75d2f28189c3941935feaf37ebaea1c9 to your computer and use it in GitHub Desktop.
Add a Progress Window to your VAADIN web application
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author | |
* | |
* Laurent de Laprade | |
* | |
* _ _ _ ____ _ | |
* | | __| | | / __ \| |__ _ __ ___ ___ ___ _ __ ___ | |
* | |/ _` | |/ / _` | '_ \| '_ \ / _ \ / __/ _ \| '_ ` _ \ | |
* | | (_| | | | (_| | | | | |_) | __/| (_| (_) | | | | | | | |
* |_|\__,_|_|\ \__,_|_| |_| .__/ \___(_)___\___/|_| |_| |_| | |
* \____/ |_| | |
* | |
* ASCII generation by: http://www.network-science.de/ascii/ | |
* | |
* A vaadin WEB application showing a page for testing the ProgressWindow | |
* | |
*/ | |
package com.mycompany.vaadin.addons; | |
import javax.servlet.annotation.WebServlet; | |
import com.vaadin.annotations.Push; | |
import com.vaadin.annotations.Theme; | |
import com.vaadin.annotations.VaadinServletConfiguration; | |
import com.vaadin.server.VaadinRequest; | |
import com.vaadin.server.VaadinServlet; | |
import com.vaadin.shared.communication.PushMode; | |
import com.vaadin.ui.Button; | |
import com.vaadin.ui.UI; | |
import com.vaadin.ui.VerticalLayout; | |
/** | |
* This UI is the application entry point. A UI may either represent a browser window | |
* (or tab) or some part of a html page where a Vaadin application is embedded. | |
* <p> | |
* The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be | |
* overridden to add component to the user interface and initialize non-component functionality. | |
*/ | |
@Theme("mytheme") | |
@Push(PushMode.MANUAL) | |
public class MyUI extends UI | |
{ | |
private static final long serialVersionUID = 2182917538601097229L; | |
void DummyLengthyOperation() throws Exception | |
{ | |
try | |
{ | |
int i=0; | |
while(i++<10 && !Thread.currentThread().isInterrupted()) Thread.sleep(500); | |
} | |
catch (InterruptedException e) | |
{ | |
System.out.println("Operation interrupted by user"); | |
} | |
} | |
@Override | |
protected void init(VaadinRequest vaadinRequest) { | |
final VerticalLayout layout = new VerticalLayout(); | |
Button button1 = new Button("Test Progress Window"); | |
button1.addClickListener | |
( | |
e -> | |
{ | |
// Initialize our new UI component | |
new ProgressWindow("Task in progress...", "Please wait") | |
{ | |
private static final long serialVersionUID = 1L; | |
@Override | |
public void lengthyOperation() throws Exception | |
{ | |
DummyLengthyOperation(); | |
} | |
}; | |
} | |
); | |
Button button2 = new Button("Test Progress Window with cancel button"); | |
button2.addClickListener | |
( | |
e -> | |
{ | |
// Initialize our new UI component | |
new ProgressWindow("Task in progress...", "Please wait", "Can't wait") | |
{ | |
private static final long serialVersionUID = 1L; | |
@Override | |
public void lengthyOperation() throws Exception | |
{ | |
DummyLengthyOperation(); | |
} | |
}; | |
} | |
); | |
layout.addComponents(button1, button2); | |
setContent(layout); | |
} | |
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true) | |
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false) | |
public static class MyUIServlet extends VaadinServlet | |
{ | |
private static final long serialVersionUID = 9104328224784918917L; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author | |
* | |
* Laurent de Laprade | |
* | |
* _ _ _ ____ _ | |
* | | __| | | / __ \| |__ _ __ ___ ___ ___ _ __ ___ | |
* | |/ _` | |/ / _` | '_ \| '_ \ / _ \ / __/ _ \| '_ ` _ \ | |
* | | (_| | | | (_| | | | | |_) | __/| (_| (_) | | | | | | | |
* |_|\__,_|_|\ \__,_|_| |_| .__/ \___(_)___\___/|_| |_| |_| | |
* \____/ |_| | |
* | |
* ASCII generation by: http://www.network-science.de/ascii/ | |
* | |
* | |
* ------------------------------------------------------------- | |
* Usage samples: | |
* ------------------------------------------------------------- | |
* Usage 1 : a Progress windows which is not cancel-able | |
* ------------------------------------------------------------- | |
* new ProgressWindow("Loading Table") | |
* { | |
* private static final long serialVersionUID = 1L; | |
* @Override | |
* public void lengthyOperation() throws Exception | |
* { | |
* ProcessLogin(); | |
* } | |
* }; | |
* ------------------------------------------------------------- | |
* Usage 2 : a Progress windows with a Cancel Button | |
* ------------------------------------------------------------- | |
* new ProgressWindow("Connecting you...", "Please wait", "Can't wait") | |
* { | |
* private static final long serialVersionUID = 1L; | |
* @Override | |
* public void lengthyOperation() throws Exception | |
* { | |
* ProcessLogin(); | |
* } | |
* }; | |
* | |
*/ | |
package com.mycompany.vaadin.addons; | |
import com.vaadin.event.FieldEvents.FocusEvent; | |
import com.vaadin.event.FieldEvents.FocusListener; | |
import com.vaadin.server.Page; | |
import com.vaadin.server.VaadinRequest; | |
import com.vaadin.server.VaadinResponse; | |
import com.vaadin.server.VaadinService; | |
import com.vaadin.server.WebBrowser; | |
import com.vaadin.ui.Alignment; | |
import com.vaadin.ui.Button.ClickEvent; | |
import com.vaadin.ui.Button.ClickListener; | |
import com.vaadin.ui.Label; | |
import com.vaadin.ui.NativeButton; | |
import com.vaadin.ui.Notification; | |
import com.vaadin.ui.Notification.Type; | |
import com.vaadin.ui.ProgressBar; | |
import com.vaadin.ui.UI; | |
import com.vaadin.ui.VerticalLayout; | |
import com.vaadin.ui.Window; | |
// | |
// PROGRESS WINDOW DESIGN | |
// drawing using http://asciiflow.com/ | |
// | |
// +---------------------------------------------------------------------------+ | |
// | | | |
// | Progress window title | CAPTION ZONE | |
// | | | |
// +---------------------------------------------------------------------------+ | |
// +---------------------------------------------------------------------------+ | |
// | | DESCRIPTION LABEL | |
// | Progess infos ... | | |
// | | | |
// | | | |
// | | | |
// | | | |
// +-----------------------+---------------------------+-----------------------+ | |
// | | | | | |
// | | progress Animation | | PROGRESS BAR | |
// | | | | | |
// | +---------------------------+ +------------------+ | | |
// | | | | | |
// | | Optional Cancel | | CANCEL BUTTON | |
// | | button | | | |
// | +------------------+ | | |
// +---------------------------------------------------------------------------+ | |
// | |
// | |
public abstract class ProgressWindow extends Window implements FocusListener, ClickListener | |
{ | |
private static final long serialVersionUID = 5253966202431615380L; | |
private VerticalLayout layout = new VerticalLayout(); | |
private Label description = new Label(); | |
private ProgressBar progressIndicator = new ProgressBar(); | |
// The lengthy action you'll implement | |
public abstract void lengthyOperation() throws Exception; | |
private boolean processWasStarted = false; | |
// members for a 'Cancel' button mode | |
// We won't use semaphore and thread spawning for | |
// the lightweight non-cancel-able progress windows mode | |
private boolean hasCancelButton = false; | |
private NativeButton btnKill = null; | |
String btnKillCaption = null; | |
Thread threadedLenghtyOperation = null; | |
public ProgressWindow(String caption) | |
{ | |
init(caption, "Please wait..."); | |
} | |
public ProgressWindow() | |
{ | |
init("Job in progress", "Please wait..."); | |
} | |
public ProgressWindow(String caption, String progressInfo) | |
{ | |
init(caption, progressInfo); | |
} | |
public ProgressWindow(String caption, String progressInfo, String killBtnText) | |
{ | |
if( UI.getCurrent().getPushConfiguration().getPushMode().isEnabled() ) | |
{ | |
hasCancelButton = true; | |
btnKillCaption = killBtnText; | |
} | |
else | |
Notification.show("Vaadin Push not enabled. 'Cancel' button cannot be added", Type.HUMANIZED_MESSAGE); | |
init(caption, progressInfo); | |
} | |
public void init(String caption, String progressInfo) | |
{ | |
setCaption(caption); | |
setClosable(false); | |
center(); | |
layout.addComponent(description); | |
description.setSizeFull(); | |
description.setHeight("40px"); | |
layout.setComponentAlignment(description, Alignment.MIDDLE_CENTER); | |
description.setValue(progressInfo); | |
layout.addComponent(progressIndicator); | |
layout.setComponentAlignment(progressIndicator, Alignment.MIDDLE_CENTER); | |
progressIndicator.setIndeterminate(true); | |
setContent(layout); | |
layout.setMargin(true); | |
setWidth("450px"); | |
setHeight("180px"); | |
setResizable(false); | |
UI.getCurrent().addWindow(this); | |
if( hasCancelButton && btnKill==null ) | |
{ | |
btnKill = new NativeButton(); | |
btnKill.setCaption(btnKillCaption); | |
layout.addComponent(btnKill); | |
layout.setComponentAlignment(btnKill, Alignment.BOTTOM_RIGHT); | |
this.setClosable(true); | |
btnKill.addClickListener(clicEvent -> buttonClick(clicEvent)); | |
} | |
// SetModal call will force focus on us, | |
// then, focus will cause the lengthy operation to start | |
setModal(true); | |
addFocusListener(this); | |
} | |
@Override | |
public void buttonClick(ClickEvent event) | |
{ | |
if( btnKill!=null && event.getButton() == btnKill ) | |
{ | |
btnKill.setEnabled(false); | |
btnKill.setCaption("Closing..."); | |
if( threadedLenghtyOperation!=null ) | |
threadedLenghtyOperation.interrupt(); | |
setModal(false); | |
close(); | |
} | |
} | |
@Override | |
public void focus(FocusEvent event) | |
{ | |
// processWasStarted flag to protect against multiple 'OnFocus' events | |
if( !processWasStarted ) | |
{ | |
try | |
{ | |
processWasStarted = true; | |
final VaadinRequest mainThreadRequest = VaadinService.getCurrentRequest(); | |
final VaadinResponse mainThreadResponse = VaadinService.getCurrentResponse(); | |
if( !hasCancelButton ) lengthyOperation(); | |
else | |
{ | |
UI currentUI = UI.getCurrent(); | |
VaadinService currentService = VaadinService.getCurrent(); | |
threadedLenghtyOperation = new Thread() | |
{ | |
@Override | |
public void run() | |
{ | |
if( currentService!=null ) VaadinService.setCurrent(currentService); | |
if( currentUI!=null ) UI.setCurrent(currentUI); | |
try | |
{ | |
// Background thread is not aware of Request/Response | |
// and this can induce bugs if your 'lengthy Operation' need them | |
// For instance if it needs to read/write cookies ! | |
if( currentService != null ) currentService.setCurrentInstances(mainThreadRequest, mainThreadResponse); | |
lengthyOperation(); | |
} | |
catch (Exception e) | |
{ | |
e.printStackTrace(); | |
} | |
finally | |
{ | |
setModal(false); | |
close(); | |
// Need push call because we are in Background thread | |
// To enable Push, Note that UI class need Push annotation like this: | |
// @Push(PushMode.MANUAL) | |
// public class MyUI extends UI | |
if( currentUI != null ) currentUI.push(); | |
} | |
} | |
}; | |
threadedLenghtyOperation.start(); | |
} | |
} | |
catch (Exception e) | |
{ | |
e.printStackTrace(); | |
} | |
finally | |
{ | |
if( btnKill==null ) | |
{ | |
// Mono-thread mode: no btnKill was added. | |
// Lengthy task is complete | |
setModal(false); | |
close(); | |
} | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment