Java tutorial
/* * Copyright 2013 Corpuslinguistic working group Humboldt University Berlin. * * 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 annis.gui.admin; import annis.gui.LoginListener; import annis.libgui.Background; import annis.libgui.Helper; import annis.service.objects.ImportJob; import com.google.common.base.Splitter; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.WebResource; import com.vaadin.data.validator.EmailValidator; import com.vaadin.server.FontAwesome; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.CheckBox; import com.vaadin.ui.FormLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Panel; import com.vaadin.ui.ProgressBar; import com.vaadin.ui.TextArea; import com.vaadin.ui.TextField; import com.vaadin.ui.UI; import com.vaadin.ui.Upload; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.BaseTheme; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import java.text.DecimalFormat; import java.util.List; import javax.ws.rs.core.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Thomas Krause <krauseto@hu-berlin.de> */ public class ImportPanel extends Panel implements Upload.ProgressListener, Upload.FinishedListener, Upload.StartedListener, Upload.Receiver, LoginListener { private static final Logger log = LoggerFactory.getLogger(ImportPanel.class); private final VerticalLayout layout; private final TextArea txtMessages; private final Upload upload; private final TextField txtMail; private final CheckBox cbOverwrite; private final TextField txtAlias; private final ProgressBar progress; private final Label lblProgress; private final Button btDetailedLog; private File temporaryCorpusFile; private boolean kickstarterMode; public ImportPanel() { setSizeFull(); layout = new VerticalLayout(); layout.setWidth("100%"); layout.setHeight("100%"); layout.setMargin(true); setContent(layout); FormLayout form = new FormLayout(); layout.addComponent(form); cbOverwrite = new CheckBox("Overwrite existing corpus"); form.addComponent(cbOverwrite); txtMail = new TextField("e-mail address for status updates"); txtMail.addValidator(new EmailValidator("Must be a valid e-mail address")); form.addComponent(txtMail); txtAlias = new TextField("alias name"); form.addComponent(txtAlias); HorizontalLayout actionBar = new HorizontalLayout(); actionBar.setSpacing(true); actionBar.setWidth("100%"); upload = new Upload("", this); upload.setButtonCaption("Upload ZIP file with relANNIS corpus and start import"); upload.setImmediate(true); upload.addStartedListener(this); upload.addFinishedListener(this); upload.setEnabled(true); actionBar.addComponent(upload); progress = new ProgressBar(); progress.setIndeterminate(true); progress.setVisible(false); actionBar.addComponent(progress); lblProgress = new Label(); lblProgress.setWidth("100%"); actionBar.addComponent(lblProgress); actionBar.setExpandRatio(lblProgress, 1.0f); actionBar.setComponentAlignment(lblProgress, Alignment.MIDDLE_LEFT); actionBar.setComponentAlignment(upload, Alignment.MIDDLE_LEFT); actionBar.setComponentAlignment(progress, Alignment.MIDDLE_LEFT); layout.addComponent(actionBar); btDetailedLog = new Button(); btDetailedLog.setStyleName(BaseTheme.BUTTON_LINK); btDetailedLog.addClickListener(new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { setLogVisible(!isLogVisible()); } }); layout.addComponent(btDetailedLog); txtMessages = new TextArea(); txtMessages.setSizeFull(); txtMessages.setValue(""); txtMessages.setReadOnly(true); layout.addComponent(txtMessages); layout.setExpandRatio(txtMessages, 1.0f); setLogVisible(false); appendMessage("Ready."); } private boolean isLogVisible() { return txtMessages.isVisible(); } private void setLogVisible(boolean visible) { txtMessages.setVisible(visible); if (visible) { btDetailedLog.setCaption("Hide log"); btDetailedLog.setIcon(FontAwesome.MINUS_SQUARE_O, "minus sign"); layout.setExpandRatio(btDetailedLog, 0.0f); } else { btDetailedLog.setCaption("Show log"); btDetailedLog.setIcon(FontAwesome.PLUS_SQUARE_O, "plus sign"); layout.setExpandRatio(btDetailedLog, 1.0f); } } private void appendMessage(String message) { lblProgress.setValue(message); txtMessages.setReadOnly(false); String oldVal = txtMessages.getValue(); if (oldVal == null || oldVal.isEmpty()) { txtMessages.setValue(message); } else { txtMessages.setValue(oldVal + "\n" + message); } txtMessages.setCursorPosition(txtMessages.getValue().length() - 1); txtMessages.setReadOnly(true); } @Override public void updateProgress(long readBytes, long contentLength) { float ratioComplete = (float) readBytes / (float) contentLength; DecimalFormat format = new DecimalFormat("#0.00"); appendMessage("uploaded " + format.format(ratioComplete * 100.0f) + "%"); } @Override public void uploadFinished(Upload.FinishedEvent event) { appendMessage("Finished upload, starting import"); startImport(); } @Override public void uploadStarted(Upload.StartedEvent event) { upload.setEnabled(false); progress.setVisible(true); progress.setEnabled(true); appendMessage("Started upload"); event.getUpload().addProgressListener(this); } @Override public OutputStream receiveUpload(String filename, String mimeType) { try { temporaryCorpusFile = File.createTempFile(filename, ".zip"); temporaryCorpusFile.deleteOnExit(); return new FileOutputStream(temporaryCorpusFile); } catch (IOException ex) { log.error(null, ex); } return null; } private void startImport() { WebResource res = Helper.getAnnisWebResource().path("admin").path("import"); String mail = txtMail.getValue(); if (txtMail.isValid() && mail != null && !mail.isEmpty()) { res = res.queryParam("statusMail", mail); } if (cbOverwrite.getValue() == true) { res = res.queryParam("overwrite", "true"); } String alias = txtAlias.getValue(); if (alias != null && !alias.isEmpty()) { res = res.queryParam("alias", alias); } try { ClientResponse response = res.entity(new FileInputStream(temporaryCorpusFile)).type("application/zip") .post(ClientResponse.class); if (response.getStatus() == Response.Status.ACCEPTED.getStatusCode()) { URI location = response.getLocation(); appendMessage("Import requested, update URL is " + location); UI ui = UI.getCurrent(); Background.run(new WaitForFinishRunner(location, ui)); } else { upload.setEnabled(true); progress.setVisible(false); appendMessage( "Error (response code " + response.getStatus() + "): " + response.getEntity(String.class)); } } catch (FileNotFoundException ex) { log.error(null, ex); } } private class WaitForFinishRunner implements Runnable { private final UI ui; private final String uuid; private int currentMessageIndex = 0; public WaitForFinishRunner(URI location, UI ui) { this.ui = ui; List<String> path = Splitter.on('/').trimResults().omitEmptyStrings().splitToList(location.getPath()); uuid = path.get(path.size() - 1); } @Override public void run() { // check the overall status WebResource res = Helper.getAnnisWebResource().path("admin").path("import").path("status"); ImportJob.Status lastStatus = ImportJob.Status.WAITING; try { while (lastStatus == ImportJob.Status.WAITING || lastStatus == ImportJob.Status.RUNNING) { lastStatus = ImportJob.Status.ERROR; List<ImportJob> jobs = res.get(new GenericType<List<ImportJob>>() { }); for (ImportJob j : jobs) { if (uuid.equals(j.getUuid())) { lastStatus = j.getStatus(); if (lastStatus == ImportJob.Status.WAITING) { appendFromBackground("Still waiting for other imports to finish..."); } else if (lastStatus == ImportJob.Status.RUNNING) { outputNewMessages(j.getMessages()); } break; } } Thread.sleep(500); } } catch (InterruptedException ex) { log.error(null, ex); } ImportJob finishInfo = res.path("finished").path(uuid).get(ImportJob.class); // print all remaining messages outputNewMessages(finishInfo.getMessages()); if (finishInfo.getStatus() == ImportJob.Status.SUCCESS) { appendFromBackground("Finished successfully."); } else if (finishInfo.getStatus() == ImportJob.Status.ERROR) { appendFromBackground("Failed."); } else { appendFromBackground("Unknown status."); } ui.access(new Runnable() { @Override public void run() { progress.setVisible(false); upload.setEnabled(true); } }); } private void outputNewMessages(List<String> allMessages) { if (currentMessageIndex < allMessages.size()) { final List<String> newMessages = allMessages.subList(currentMessageIndex, allMessages.size()); currentMessageIndex = allMessages.size(); appendFromBackground(newMessages); } } private void appendFromBackground(final String message) { ui.access(new Runnable() { @Override public void run() { appendMessage(message); } }); } private void appendFromBackground(List<String> message) { for (String m : message) { appendFromBackground(m); } } } @Override public void onLogin() { if (!kickstarterMode) { upload.setEnabled(true); } } @Override public void onLogout() { if (!kickstarterMode) { upload.setEnabled(false); } } public void updateMode(boolean kickstarterMode, boolean isLoggedIn) { this.kickstarterMode = kickstarterMode; if (kickstarterMode) { upload.setEnabled(true); } else { upload.setEnabled(isLoggedIn); } } }