/** * Copyright (c) 2011, 2012 Gunnar Wagenknecht and others. * All rights reserved. * * This program and the accompanying materials are made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Gunnar Wagenknecht - initial API and implementation */ package org.eclipse.gyrex.admin.ui.internal.wizards.dialogfields; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.rap.rwt.RWT; import org.eclipse.rap.rwt.service.ServerPushSession; import org.eclipse.rap.rwt.service.ServiceHandler; import org.eclipse.rap.rwt.widgets.FileUpload; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.apache.commons.fileupload.FileItemHeaders; import org.apache.commons.fileupload.FileItemHeadersSupport; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang.math.NumberUtils; /** * DialogField using RWT Upload widget */ public class UploadDialogField extends DialogField { private interface IUploadHandlerListener { void uploadFailed(Throwable e); void uploadFinished(String fileName); } /** * RAP handler for receiving uploads. */ private final class UploadHandler implements ServiceHandler { private final String handlerId; private final IUploadAdapter uploadAdapter; private IUploadHandlerListener listener; public UploadHandler(final IUploadAdapter uploadAdapter) { this.uploadAdapter = uploadAdapter; handlerId = UploadHandler.class.getName() + "@" + System.identityHashCode(this); if (!uploadHandlerRef.compareAndSet(null, this)) throw new IllegalStateException("Concurrent upload in progress!"); RWT.getServiceManager().registerServiceHandler(handlerId, this); } public void dispose() { uploadHandlerRef.compareAndSet(this, null); RWT.getServiceManager().unregisterServiceHandler(handlerId); } private long getContentLength(final FileItemHeadersSupport itemWithHeaders) { final FileItemHeaders headers = itemWithHeaders.getHeaders(); return NumberUtils.toLong(null != headers ? headers.getHeader(FileUploadBase.CONTENT_LENGTH) : null, -1L); } public String getUploadUrl() { final StringBuilder url = new StringBuilder(RWT.getServiceManager().getServiceHandlerUrl(handlerId)); final int relativeIndex = url.lastIndexOf("/"); if (relativeIndex > -1) { url.delete(0, relativeIndex + 1); } return RWT.getResponse().encodeURL(url.toString()); } @Override public void service(final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException { // Ignore requests to this service handler without a valid session for security reasons final boolean hasSession = request.getSession(false) != null; if (!hasSession) { response.sendError(HttpServletResponse.SC_BAD_REQUEST); return; } if (!"POST".equals(request.getMethod().toUpperCase())) { response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); return; } if (!ServletFileUpload.isMultipartContent(request)) { response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); return; } final ServletFileUpload servletFileUpload = new ServletFileUpload(); servletFileUpload.setFileSizeMax(1024 * 1024 * 5); // 5 MB try { final FileItemIterator itemIterator = servletFileUpload.getItemIterator(request); while (itemIterator.hasNext()) { final FileItemStream item = itemIterator.next(); if (item.isFormField()) { continue; // skip form fields (just files) } final InputStream stream = item.openStream(); try { uploadAdapter.receive(stream, item.getName(), item.getContentType(), getContentLength(item)); } finally { IOUtils.closeQuietly(stream); } // at most one file will be received; unregister the handler and return try { if (null != listener) { listener.uploadFinished(item.getName()); } } finally { dispose(); } return; } } catch (final VirtualMachineError e) { throw e; } catch (final Throwable t) { if (null != listener) { listener.uploadFailed(t); } throw new ServletException("Error retrieving files.", t); } // no files? response.sendError(HttpServletResponse.SC_BAD_REQUEST); } public void setListener(final IUploadHandlerListener listener) { this.listener = listener; } } protected static GridData gridDataForUpload(final int span) { final GridData gd = new GridData(); gd.horizontalAlignment = GridData.FILL; gd.grabExcessHorizontalSpace = false; gd.horizontalSpan = span; return gd; } final AtomicReference<UploadHandler> uploadHandlerRef = new AtomicReference<UploadHandler>(); private String uploadButtonLabel; private Text fileText; private FileUpload uploadControl; private boolean uploadInProgress; private String fileName = ""; public UploadDialogField() { super(); uploadButtonLabel = "Browse..."; //$NON-NLS-1$ } @Override public Control[] doFillIntoGrid(final Composite parent, final int nColumns) { assertEnoughColumns(nColumns); final Label label = getLabelControl(parent); label.setLayoutData(gridDataForLabel(1)); final Text text = getFileTextControl(parent); text.setLayoutData(StringDialogField.gridDataForText(nColumns - 2)); final FileUpload upload = getUploadControl(parent); upload.setLayoutData(gridDataForUpload(1)); return new Control[] { label, text, upload }; } private void doModifyFileName() { if (isOkToUse(uploadControl)) { fileName = StringUtils.trimToEmpty(uploadControl.getFileName()); fileText.setText(fileName); } dialogFieldChanged(); } /** * Returns the name of the selected file. */ public String getFileName() { return fileName; } public Text getFileTextControl(final Composite parent) { if (fileText == null) { assertCompositeNotNull(parent); fileText = new Text(parent, SWT.BORDER | SWT.SINGLE); fileText.setText(fileName); fileText.setToolTipText("Selected file"); fileText.setEditable(false); fileText.setFont(parent.getFont()); fileText.setEnabled(isEnabled()); } return fileText; } @Override public int getNumberOfControls() { return 3; } /** * Creates or returns the created text control. * * @param parent * The parent composite or <code>null</code> when the widget has * already been created. * @return the text control */ public FileUpload getUploadControl(final Composite parent) { if (uploadControl == null) { assertCompositeNotNull(parent); uploadControl = new FileUpload(parent, SWT.NONE); uploadControl.setText(uploadButtonLabel); uploadControl.setToolTipText("Select a file"); uploadControl.setFont(parent.getFont()); uploadControl.addSelectionListener(new SelectionAdapter() { /** serialVersionUID */ private static final long serialVersionUID = 1L; @Override public void widgetSelected(final SelectionEvent e) { doModifyFileName(); } }); } return uploadControl; } @Override public void refresh() { super.refresh(); if (isOkToUse(uploadControl)) { } } @Override public boolean setFocus() { if (isOkToUse(uploadControl)) { uploadControl.setFocus(); } return true; } /** * Sets the uploadButtonLabel. * * @param uploadButtonLabel * the uploadButtonLabel to set */ public void setUploadButtonLabel(final String uploadButtonLabel) { this.uploadButtonLabel = uploadButtonLabel; if (isOkToUse(uploadControl)) { uploadControl.setText(uploadButtonLabel); } } public void startUpload(final IUploadAdapter receiver) { uploadInProgress = true; updateEnableState(); // activate background updates final ServerPushSession pushSession = new ServerPushSession(); // create handler final UploadHandler handler = new UploadHandler(receiver); uploadControl.addDisposeListener(new DisposeListener() { /** serialVersionUID */ private static final long serialVersionUID = 1L; @Override public void widgetDisposed(final DisposeEvent event) { handler.dispose(); pushSession.stop(); } }); final Display display = uploadControl.getDisplay(); final String url = handler.getUploadUrl(); handler.setListener(new IUploadHandlerListener() { @Override public void uploadFailed(final Throwable e) { handler.dispose(); display.asyncExec(new Runnable() { @Override public void run() { fileText.setText(String.format("ERROR: %s", ExceptionUtils.getRootCauseMessage(e))); uploadInProgress = false; setUploadButtonLabel(uploadButtonLabel); updateEnableState(); pushSession.stop(); } }); } @Override public void uploadFinished(final String fileName) { handler.dispose(); display.asyncExec(new Runnable() { @Override public void run() { if (isOkToUse(fileText)) { fileText.setText(getFileName()); } uploadInProgress = false; setUploadButtonLabel(uploadButtonLabel); updateEnableState(); pushSession.stop(); } }); } }); // start upload pushSession.start(); uploadControl.setText("Uploading..."); uploadControl.submit(url); } @Override protected void updateEnableState() { super.updateEnableState(); if (isOkToUse(fileText)) { fileText.setEnabled(isEnabled()); } if (isOkToUse(uploadControl)) { uploadControl.setEnabled(isEnabled() && !uploadInProgress); } } }