/*******************************************************************************
* Copyright (c) 2012 Pivotal Software, Inc.
* 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:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package com.vmware.vfabric.ide.eclipse.tcserver.internal.ui;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jst.server.tomcat.core.internal.WebModule;
import org.eclipse.jst.server.tomcat.core.internal.command.ModifyWebModuleCommand;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.internal.WorkbenchImages;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.ui.editor.ServerEditorSection;
import com.vmware.vfabric.ide.eclipse.tcserver.internal.core.TcServer;
/**
* Allows to configure filename patterns to control reloading of web
* applications.
* @author Christian Dupuis
*/
@SuppressWarnings("restriction")
public class ExtendedTcStaticResourcesEditorSection extends ServerEditorSection {
class FilenameLabelProvider extends LabelProvider {
@Override
public Image getImage(Object element) {
return WorkbenchImages.getImage(ISharedImages.IMG_OBJ_FILE);
}
@Override
public String getText(Object element) {
return element.toString();
}
}
class StaticFilenamesContentProvider implements ITreeContentProvider {
public void dispose() {
}
public Object[] getChildren(Object parentElement) {
return getElements(parentElement);
}
public Object[] getElements(Object inputElement) {
if (inputElement instanceof IServer) {
IServer server = (IServer) inputElement;
TcServer tcServer = (TcServer) server.loadAdapter(TcServer.class, null);
String[] filenames = StringUtils.splitByWholeSeparator(tcServer.getStaticFilenamePatterns(), ",");
return filenames;
}
return new Object[0];
}
public Object getParent(Object element) {
return null;
}
public boolean hasChildren(Object element) {
return getChildren(element).length > 0;
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
protected TcServer serverWorkingCopy;
private Table filenameTable;
private TableViewer filenamesTableViewer;
private Button addButton;
private Button deleteButton;
private Button upButton;
private Button downButton;
protected boolean updating;
protected PropertyChangeListener listener;
private StaticFilenamesContentProvider contentProvider;
private Button enableButton;
private Button enableAgentButton;
private Text agentOptionsText;
protected void addConfigurationChangeListener() {
listener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (TcServer.PROPERTY_ENHANCED_REDEPLOY.equals(event.getPropertyName())) {
updateEnablement();
}
else if (TcServer.PROPERTY_AGENT_REDEPLOY.equals(event.getPropertyName())) {
updateEnablement();
}
else if (TcServer.PROPERTY_STATIC_FILENAMES.equals(event.getPropertyName())) {
filenamesTableViewer.setInput(server);
}
else if (TcServer.PROPERTY_AGENT_OPTIONS.equals(event.getPropertyName())) {
if (!updating) {
updating = true;
agentOptionsText.setText(serverWorkingCopy.getAgentOptions());
agentOptionsText.setSelection(agentOptionsText.getText().length());
updating = false;
}
}
}
};
serverWorkingCopy.getServerWorkingCopy().addPropertyChangeListener(listener);
}
@Override
public void createSection(Composite parent) {
super.createSection(parent);
FormToolkit toolkit = getFormToolkit(parent.getDisplay());
Section section = toolkit.createSection(parent, ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED
| ExpandableComposite.TITLE_BAR | Section.DESCRIPTION | ExpandableComposite.FOCUS_TITLE);
section.setText("Application Reload Behavior");
section.setDescription("Configure application reload behavior on changes to project resources.");
section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL));
Composite composite = toolkit.createComposite(section);
GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.marginHeight = 5;
layout.marginWidth = 1;
layout.verticalSpacing = 5;
layout.horizontalSpacing = 10;
composite.setLayout(layout);
composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL));
toolkit.paintBordersFor(composite);
section.setClient(composite);
enableAgentButton = toolkit.createButton(composite, "Enable Java Agent-based reloading (experimental)",
SWT.CHECK);
GridDataFactory.fillDefaults().span(2, 1).applyTo(enableAgentButton);
enableAgentButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
if (updating) {
return;
}
try {
updating = true;
boolean enabled = enableAgentButton.getSelection();
execute(new ModifyAgentRedeployCommand(serverWorkingCopy, enabled));
try {
List<?> list = serverWorkingCopy.getTomcatConfiguration().getWebModules();
int i = 0;
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()) {
WebModule module = (WebModule) iterator.next();
WebModule newModule = new WebModule(module.getPath(), module.getDocumentBase(), module
.getMemento(), !enabled);
execute(new ModifyWebModuleCommand(serverWorkingCopy.getTomcatConfiguration(), i, newModule));
i++;
}
}
catch (Exception e) {
return;
}
}
finally {
updating = false;
}
}
});
Composite optionsComposite = new Composite(composite, SWT.NONE);
GridLayout optionsLayout = new GridLayout(2, false);
optionsLayout.marginWidth = 1;
optionsLayout.marginHeight = 2;
optionsComposite.setLayout(optionsLayout);
GridDataFactory.fillDefaults().applyTo(optionsComposite);
toolkit.paintBordersFor(optionsComposite);
toolkit.createLabel(optionsComposite, "Options:");
agentOptionsText = toolkit.createText(optionsComposite, "");
GridDataFactory.fillDefaults().grab(true, false).applyTo(agentOptionsText);
agentOptionsText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
if (updating) {
return;
}
try {
updating = true;
String value = agentOptionsText.getText();
execute(new ModifyAgentOptionsCommand(serverWorkingCopy, value));
}
finally {
updating = false;
}
}
});
enableButton = toolkit.createButton(composite, "Enable JMX-based reloading", SWT.CHECK);
GridDataFactory.fillDefaults().span(2, 1).applyTo(enableButton);
enableButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
if (updating) {
return;
}
try {
updating = true;
boolean enabled = enableButton.getSelection();
execute(new ModifyEnhancedRedeployCommand(serverWorkingCopy, enabled));
try {
List<?> list = serverWorkingCopy.getTomcatConfiguration().getWebModules();
int i = 0;
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()) {
WebModule module = (WebModule) iterator.next();
WebModule newModule = new WebModule(module.getPath(), module.getDocumentBase(), module
.getMemento(), !enabled);
execute(new ModifyWebModuleCommand(serverWorkingCopy.getTomcatConfiguration(), i, newModule));
i++;
}
}
catch (Exception e) {
return;
}
}
finally {
updating = false;
}
}
});
Label enhancedRedeploymentLabel = toolkit
.createLabel(
composite,
"Define patterns for files that should be copied to deployed applications without triggereing a reload.\nSpring XML configuration files are handled separately.");
GridDataFactory.fillDefaults().span(2, 1).applyTo(enhancedRedeploymentLabel);
filenameTable = toolkit.createTable(composite, SWT.SINGLE | SWT.V_SCROLL | SWT.FULL_SELECTION);
GridData data = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING);
data.heightHint = 130;
filenameTable.setLayoutData(data);
filenamesTableViewer = new TableViewer(filenameTable);
contentProvider = new StaticFilenamesContentProvider();
filenamesTableViewer.setContentProvider(contentProvider);
filenamesTableViewer.setLabelProvider(new FilenameLabelProvider());
filenamesTableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
Object obj = ((IStructuredSelection) event.getSelection()).getFirstElement();
deleteButton.setEnabled(obj != null);
updateButtons(obj);
}
});
Composite buttonComposite = new Composite(composite, SWT.NONE);
GridLayout buttonLayout = new GridLayout(1, true);
buttonLayout.marginWidth = 0;
buttonLayout.marginHeight = 0;
buttonComposite.setLayout(buttonLayout);
GridDataFactory.fillDefaults().applyTo(buttonComposite);
addButton = toolkit.createButton(buttonComposite, "Add...", SWT.PUSH);
GridDataFactory.fillDefaults().applyTo(addButton);
addButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (updating) {
return;
}
InputDialog dialog = new InputDialog(
getShell(),
"New filename pattern",
"Enter a new filename pattern for static resources (wildcards such as '*' and '?' are supported):",
"", new IInputValidator() {
public String isValid(String newText) {
if (StringUtils.isBlank(newText)) {
return "Pattern can't be empty";
}
return null;
}
});
if (dialog.open() == Dialog.OK) {
updating = true;
List<Object> filenames = new ArrayList<Object>(Arrays.asList(contentProvider.getElements(server)));
filenames.add(dialog.getValue());
execute(new ModifyStaticResourcesCommand(serverWorkingCopy, StringUtils.join(filenames, ",")));
filenamesTableViewer.setInput(server);
filenamesTableViewer.setSelection(new StructuredSelection(dialog.getValue()));
// update buttons
updating = false;
}
}
});
deleteButton = toolkit.createButton(buttonComposite, "Delete", SWT.PUSH);
GridDataFactory.fillDefaults().applyTo(deleteButton);
deleteButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Object selectedArtefact = ((IStructuredSelection) filenamesTableViewer.getSelection())
.getFirstElement();
if (updating) {
return;
}
updating = true;
List<Object> filenames = new ArrayList<Object>(Arrays.asList(contentProvider.getElements(server)));
filenames.remove(selectedArtefact);
execute(new ModifyStaticResourcesCommand(serverWorkingCopy, StringUtils.join(filenames, ",")));
filenamesTableViewer.setInput(server);
// update buttons
updating = false;
}
});
upButton = toolkit.createButton(buttonComposite, "Up", SWT.PUSH);
GridDataFactory.fillDefaults().applyTo(upButton);
upButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Object selectedArtefact = ((IStructuredSelection) filenamesTableViewer.getSelection())
.getFirstElement();
List<Object> modules = new ArrayList<Object>();
modules.addAll(Arrays.asList(contentProvider.getElements(server)));
int index = modules.indexOf(selectedArtefact);
modules.remove(selectedArtefact);
modules.add(index - 1, selectedArtefact);
if (updating) {
return;
}
updating = true;
execute(new ModifyStaticResourcesCommand(serverWorkingCopy, StringUtils.join(modules, ",")));
filenamesTableViewer.setInput(server);
updateButtons(selectedArtefact);
updating = false;
}
});
downButton = toolkit.createButton(buttonComposite, "Down", SWT.PUSH);
GridDataFactory.fillDefaults().applyTo(downButton);
downButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Object selectedArtefact = ((IStructuredSelection) filenamesTableViewer.getSelection())
.getFirstElement();
List<Object> modules = new ArrayList<Object>();
modules.addAll(Arrays.asList(contentProvider.getElements(server)));
int index = modules.indexOf(selectedArtefact);
modules.remove(selectedArtefact);
modules.add(index + 1, selectedArtefact);
if (updating) {
return;
}
updating = true;
execute(new ModifyStaticResourcesCommand(serverWorkingCopy, StringUtils.join(modules, ",")));
filenamesTableViewer.setInput(server);
updateButtons(selectedArtefact);
updating = false;
}
});
FormText restoreDefault = toolkit.createFormText(composite, true);
GridDataFactory.fillDefaults().span(2, 1).applyTo(restoreDefault);
restoreDefault.setText("<form><p><a href=\"exportbundle\">Restore default</a> filename patterns</p></form>",
true, false);
restoreDefault.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
updating = true;
execute(new ModifyStaticResourcesCommand(serverWorkingCopy, TcServer.DEFAULT_STATIC_FILENAMES));
filenamesTableViewer.setInput(server);
updating = false;
}
});
updateEnablement();
initialize();
}
/**
* @see ServerEditorSection#dispose()
*/
@Override
public void dispose() {
if (server != null) {
server.removePropertyChangeListener(listener);
}
}
/**
* @see ServerEditorSection#init(IEditorSite, IEditorInput)
*/
@Override
public void init(IEditorSite site, IEditorInput input) {
super.init(site, input);
serverWorkingCopy = (TcServer) server.loadAdapter(TcServer.class, null);
addConfigurationChangeListener();
}
/**
* Initialize the fields in this editor.
*/
protected void initialize() {
updating = true;
enableButton.setSelection(serverWorkingCopy.isEnhancedRedeployEnabled());
enableAgentButton.setSelection(serverWorkingCopy.isAgentRedeployEnabled());
agentOptionsText.setText(serverWorkingCopy.getAgentOptions());
filenamesTableViewer.setInput(server);
deleteButton.setEnabled(false);
updating = false;
}
private void updateButtons() {
IStructuredSelection selection = (IStructuredSelection) filenamesTableViewer.getSelection();
Object selectedArtefact = selection.getFirstElement();
updateButtons(selectedArtefact);
}
private void updateButtons(Object obj) {
if (obj instanceof String) {
List<Object> modules = Arrays.asList(contentProvider.getElements(server));
int index = modules.indexOf(obj);
upButton.setEnabled(index > 0);
downButton.setEnabled(index < modules.size() - 1);
deleteButton.setEnabled(true);
}
else {
upButton.setEnabled(false);
downButton.setEnabled(false);
deleteButton.setEnabled(false);
}
}
private void updateEnablement() {
boolean agentEnabled = serverWorkingCopy.isAgentRedeployEnabled();
boolean jmxEnabled = serverWorkingCopy.isEnhancedRedeployEnabled();
if (!jmxEnabled) {
filenamesTableViewer.setSelection(StructuredSelection.EMPTY);
}
filenameTable.setEnabled(jmxEnabled);
addButton.setEnabled(jmxEnabled);
agentOptionsText.setEnabled(agentEnabled);
enableButton.setEnabled(!agentEnabled);
enableAgentButton.setEnabled(!jmxEnabled);
updateButtons();
}
}