/*******************************************************************************
* Copyright (c) 2012, 2016 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.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jst.server.tomcat.core.internal.ITomcatServer;
import org.eclipse.osgi.util.NLS;
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.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.SharedScrolledComposite;
import org.eclipse.wst.server.core.IRuntime;
import org.eclipse.wst.server.core.IServerWorkingCopy;
import org.eclipse.wst.server.core.TaskModel;
import org.eclipse.wst.server.core.internal.ServerWorkingCopy;
import org.eclipse.wst.server.ui.wizard.IWizardHandle;
import org.eclipse.wst.server.ui.wizard.WizardFragment;
import org.springsource.ide.eclipse.commons.core.FileUtil;
import org.springsource.ide.eclipse.commons.core.StatusHandler;
import org.springsource.ide.eclipse.commons.ui.UiUtil;
import com.vmware.vfabric.ide.eclipse.tcserver.internal.core.TcServer;
import com.vmware.vfabric.ide.eclipse.tcserver.internal.core.TcServerRuntime;
import com.vmware.vfabric.ide.eclipse.tcserver.internal.core.TcServerUtil;
import com.vmware.vfabric.ide.eclipse.tcserver.internal.core.TemplatePropertiesReader;
import com.vmware.vfabric.ide.eclipse.tcserver.internal.core.TemplateProperty;
import com.vmware.vfabric.ide.eclipse.tcserver.internal.ui.TcServer21InstanceCreationFragment.InstanceConfiguration.Layout;
/**
* @author Steffen Pingel
* @author Christian Dupuis
* @author Tomasz Zarna
*/
public class TcServer21InstanceCreationFragment extends WizardFragment {
public static final String INSTANCE_CONFIGURATION = "instanceConfiguration";
public static final String SPECIFY_INSTANCE_PARAMETERS_MESSAGE = "Specify instance parameters.";
public static final String SELECT_TEMPLATE_MESSAGE = "Select one or more templates.";
public static final String LOCATION_DOES_NOT_EXIST_MESSAGE = "The specified location does not exist.";
private static Pattern VALID_CHARS = Pattern.compile("[\\p{Alnum}-][\\p{Alnum}-_]*");
public static class InstanceConfiguration {
public enum Layout {
SEPARATE, COMBINED
};
private Set<String> templates;
private Layout layout;
private String name;
Set<TemplateProperty> templateProperties = new HashSet<TemplateProperty>();
private String instanceDir;
public Set<String> getTemplates() {
return Collections.unmodifiableSet(templates);
}
public Layout getLayout() {
return layout;
}
public String getName() {
return name;
}
public Set<TemplateProperty> getTemplateProperties() {
return Collections.unmodifiableSet(templateProperties);
}
public String getInstanceDir() {
return instanceDir;
}
}
private CheckboxTableViewer templateViewer;
protected Set<String> templates = Collections.emptySet();
private Button separateLayoutButton;
private Button combinedLayoutButton;
private IRuntime runtime;
private Text nameText;
private IWizardHandle wizardHandle;
private IServerWorkingCopy wc;
private Text readmeText;
private Label readmeLabel;
// private WizardFragment instancePropertiesPage;
private Button defaultLocationCheckbox;
private Label locationLabel;
private Combo locationPathField;
private Button locationBrowseButton;
private boolean isDefaultServerName;
protected TcServer21InstanceCreationFragment() {
setComplete(false);
}
@Override
public boolean hasComposite() {
return true;
}
@Override
public Composite createComposite(Composite parent, IWizardHandle handle) {
this.wizardHandle = handle;
handle.setTitle("Create tc Server Instance");
handle.setDescription(SPECIFY_INSTANCE_PARAMETERS_MESSAGE);
handle.setImageDescriptor(TcServerImages.WIZB_SERVER);
SharedScrolledComposite scroller = new SharedScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL) {};
// scroller.setWidthHint(500); // Avoid excessively wide dialogs
// Display display = Display.getCurrent();
// Color blue = display.getSystemColor(SWT.COLOR_BLUE);
// scroller.setBackground(blue);
scroller.setExpandHorizontal(true);
scroller.setExpandVertical(true);
Composite page = new Composite(scroller, SWT.NONE);
page.setLayout(new GridLayout(3, false));
Label label = new Label(page, SWT.NONE);
label.setText("Name:");
nameText = new Text(page, SWT.BORDER);
GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(nameText);
nameText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
validate();
((ServerWorkingCopy) wc).setAttribute(TcServer.KEY_SERVER_NAME, nameText.getText());
}
});
label = new Label(page, SWT.NONE);
label.setText("Templates:");
templateViewer = CheckboxTableViewer.newCheckList(page, SWT.BORDER);
GC gc = new GC(page);
FontMetrics fm = gc.getFontMetrics();
int textLineHeight = fm.getHeight();
int hintHeight = textLineHeight * 13; // We don't yet know how big
// templates array is, so use
// something reasonable
gc.dispose();
GridDataFactory.fillDefaults().span(3, 1).grab(true, true).hint(SWT.DEFAULT, hintHeight)
.applyTo(templateViewer.getControl());
templateViewer.setContentProvider(new IStructuredContentProvider() {
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
// ignore
}
public void dispose() {
// ignore
}
public Object[] getElements(Object inputElement) {
return templates.toArray();
}
});
// templateViewer.setLabelProvider(new ChildLabelProvider());
templateViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
validate();
ISelection selection = event.getSelection();
TcServerRuntime tcRuntime = (TcServerRuntime) runtime.loadAdapter(TcServerRuntime.class,
new NullProgressMonitor());
if (selection instanceof StructuredSelection && tcRuntime != null) {
Object obj = ((StructuredSelection) selection).getFirstElement();
File templateDir = obj instanceof String ? tcRuntime.getTemplateFolder((String) obj) : null;
if (templateDir != null && templateDir.exists() && templateDir.isDirectory()) {
readmeLabel.setText("Information for template " + templateDir.getName() + ": ");
File readmeFile = new File(templateDir.getPath().concat("/README.txt"));
try {
String readmeFileContents = FileUtil.readFile(readmeFile, new NullProgressMonitor());
readmeText.setText(readmeFileContents);
}
catch (CoreException e) {
readmeText.setText(NLS.bind(
"Could not read information file for {0}.\n\nCheck permissions on {1}",
templateDir.getName(), readmeFile));
}
updateChildFragments();
}
else {
readmeText.setText("");
}
}
}
});
templateViewer.setSorter(new ViewerSorter());
Link link = new Link(page, SWT.NONE);
String message = "(for more templates, please visit the <a>tc server documentation</a>)";
link.setText(message);
link.addSelectionListener(new SelectionAdapter(){
@Override
public void widgetSelected(SelectionEvent e) {
UiUtil.openUrl("http://tcserver.docs.pivotal.io/docs-tcserver/topics/template.html#managing-templates");
}
});
GridDataFactory.fillDefaults().span(3, 1).align(SWT.END, SWT.BEGINNING).applyTo(link);
readmeLabel = new Label(page, SWT.NONE);
GridDataFactory.fillDefaults().span(3, 1).grab(true, false).applyTo(readmeLabel);
readmeLabel.setText("Template information:");
readmeText = new Text(page, SWT.V_SCROLL | SWT.BORDER | SWT.READ_ONLY | SWT.WRAP);
gc = new GC(page);
fm = gc.getFontMetrics();
textLineHeight = fm.getHeight();
gc.dispose();
GridDataFactory.fillDefaults().span(3, 1).grab(true, false).hint(SWT.DEFAULT, textLineHeight * 7)
.applyTo(readmeText);
readmeText.setText("Click on a template to see information about that template.");
Group layoutGroup = new Group(page, SWT.BORDER);
GridDataFactory.fillDefaults().span(3, 1).grab(true, false).applyTo(layoutGroup);
layoutGroup.setLayout(new GridLayout(1, false));
layoutGroup.setText("Layout");
separateLayoutButton = new Button(layoutGroup, SWT.RADIO);
separateLayoutButton.setText("Separate (recommended default)");
combinedLayoutButton = new Button(layoutGroup, SWT.RADIO);
combinedLayoutButton.setText("Combined");
defaultLocationCheckbox = new Button(page, SWT.CHECK | SWT.LEFT);
defaultLocationCheckbox.setSelection(true);
defaultLocationCheckbox.setText("Use default instance location");
GridDataFactory.fillDefaults().span(3, 1).applyTo(defaultLocationCheckbox);
defaultLocationCheckbox.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
boolean checked = defaultLocationCheckbox.getSelection();
if (checked) {
locationPathField.setText(runtime.getLocation().toOSString());
((ServerWorkingCopy) wc).setAttribute(ITomcatServer.PROPERTY_INSTANCE_DIR, (String) null);
}
validate();
locationLabel.setEnabled(!checked);
locationPathField.setEnabled(!checked);
locationBrowseButton.setEnabled(!checked);
}
});
locationLabel = new Label(page, SWT.NONE);
locationLabel.setText("Location:");
GridData data = new GridData();
locationLabel.setLayoutData(data);
locationLabel.setEnabled(false);
locationPathField = new Combo(page, SWT.DROP_DOWN);
locationPathField.setEnabled(false);
data = new GridData(GridData.FILL_HORIZONTAL);
locationPathField.setLayoutData(data);
locationPathField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
validate();
((ServerWorkingCopy) wc).setAttribute(ITomcatServer.PROPERTY_INSTANCE_DIR, locationPathField.getText());
}
});
locationBrowseButton = new Button(page, SWT.PUSH);
locationBrowseButton.setText("Browse...");
data = new GridData();
locationBrowseButton.setLayoutData(data);
locationBrowseButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
handleLocationBrowseButtonPressed();
validate();
}
});
locationBrowseButton.setEnabled(false);
Dialog.applyDialogFont(page);
page.pack(true);
scroller.setContent(page);
return scroller;
}
private void handleLocationBrowseButtonPressed() {
DirectoryDialog dialog = new DirectoryDialog(locationBrowseButton.getShell());
dialog.setMessage("Select location");
String dirName = locationPathField.getText();
if (!dirName.equals("")) {//$NON-NLS-1$
File path = new File(dirName);
if (path.exists()) {
dialog.setFilterPath(dirName);
}
}
String selectedDirectory = dialog.open();
if (selectedDirectory != null) {
locationPathField.setText(selectedDirectory);
((ServerWorkingCopy) wc).setAttribute(ITomcatServer.PROPERTY_INSTANCE_DIR, selectedDirectory);
}
}
protected void validate() {
if (nameText.getText().trim().length() == 0) {
wizardHandle.setMessage(TcServerInstanceConfiguratorPage.ENTER_NAME, IMessageProvider.NONE);
}
else if (!isDefaultLocationSelected() && !isValidLocation(locationPathField.getText())) {
wizardHandle.setMessage(LOCATION_DOES_NOT_EXIST_MESSAGE, IMessageProvider.ERROR);
}
else if (instanceExists()) {
wizardHandle.setMessage(TcServerInstanceConfiguratorPage.INSTANCE_EXISTS, IMessageProvider.ERROR);
}
else if (hasInvalidCharacters(nameText.getText())) {
wizardHandle.setMessage(TcServerInstanceConfiguratorPage.ILLEGAL_SERVER_NAME, IMessageProvider.ERROR);
}
else if (templateViewer.getCheckedElements().length == 0) {
wizardHandle.setMessage(SELECT_TEMPLATE_MESSAGE, IMessageProvider.NONE);
}
else {
wizardHandle.setMessage(null, IMessageProvider.NONE);
}
setComplete(wizardHandle.getMessage() == null);
wizardHandle.update();
}
private boolean hasInvalidCharacters(String text) {
return !VALID_CHARS.matcher(text).matches();
}
private boolean instanceExists() {
IPath path = null;
if (isDefaultLocationSelected()) {
if (runtime == null) {
return false;
}
path = runtime.getLocation();
}
else {
path = new Path(locationPathField.getText());
}
path = path.append(nameText.getText());
return path.toFile().exists();
}
private boolean isDefaultLocationSelected() {
return defaultLocationCheckbox.getSelection();
}
private boolean isValidLocation(String location) {
return new File(location).exists();
}
private InstanceConfiguration initModel() {
InstanceConfiguration model = new InstanceConfiguration();
model.name = nameText.getText();
model.templates = new HashSet<String>();
for (Object element : templateViewer.getCheckedElements()) {
model.templates.add((String) element);
}
model.layout = (separateLayoutButton.getSelection()) ? Layout.SEPARATE : Layout.COMBINED;
if (!defaultLocationCheckbox.getSelection()) {
model.instanceDir = locationPathField.getText();
}
return model;
}
@Override
public void enter() {
this.wc = (IServerWorkingCopy) getTaskModel().getObject(TaskModel.TASK_SERVER);
this.runtime = wc.getRuntime();
initialize();
validate();
}
@Override
public void exit() {
if (isDefaultServerName) {
TcServerUtil.setTcServerDefaultName(wc);
}
InstanceConfiguration model = initModel();
getTaskModel().putObject(INSTANCE_CONFIGURATION, model);
}
private void initialize() {
isDefaultServerName = wc == null || TcServerUtil.isTcServerDefaultName(wc);
if (runtime == null) {
setComplete(false);
return;
}
if (!separateLayoutButton.isDisposed()) {
separateLayoutButton.setSelection(true);
}
TcServerRuntime tcRuntime = (TcServerRuntime) runtime.loadAdapter(TcServerRuntime.class,
new NullProgressMonitor());
if (tcRuntime != null) {
templates = tcRuntime.getTemplates();
}
else {
templates = Collections.emptySet();
}
templateViewer.setInput(templates);
locationPathField.setText(runtime.getLocation().toOSString());
if (wc != null) {
wc.setName(nameText.getText());
}
}
@Override
public void performFinish(IProgressMonitor monitor) throws CoreException {
// reset completion status in case the wizard is re-used
setComplete(false);
InstanceConfiguration model = (InstanceConfiguration) getTaskModel().getObject(INSTANCE_CONFIGURATION);
if (model == null) {
return;
}
IPath runtimeLocation = runtime.getLocation();
List<String> arguments = new ArrayList<String>();
arguments.add("create");
arguments.add(model.getName());
String tomcatVersion = TcServerUtil.getServerVersion(runtime);
if (tomcatVersion != null) {
arguments.add("-v");
arguments.add(tomcatVersion);
}
for (String template : model.getTemplates()) {
arguments.add("-t");
arguments.add(template);
}
for (TemplateProperty prop : model.getTemplateProperties()) {
if (!prop.isDefault()) {
arguments.add("--property");
arguments.add(prop.getTemplate() + "." + prop.getKey() + "=" + prop.getValue());
}
}
String instanceDir = model.getInstanceDir();
if (instanceDir != null) {
arguments.add("-i");
arguments.add(instanceDir);
}
IVMInstall vmInstall = TcServerRuntime.getVM(runtime);
if (vmInstall != null) {
arguments.add("--java-home");
arguments.add(vmInstall.getInstallLocation().getPath());
}
TcServerUtil.executeInstanceCreation(runtimeLocation, model.getName(),
arguments.toArray(new String[arguments.size()]));
// start server inline for new instances
((ServerWorkingCopy) wc).setAttribute(ITomcatServer.PROPERTY_TEST_ENVIRONMENT, false);
// load the configuration from the directory based on the selections
// made on the wizard page
TcServerUtil.importRuntimeConfiguration(wc, monitor);
}
private Set<String> getCheckedTemplateNames() {
Set<String> checkedTemplateNames = new HashSet<String>();
if (templateViewer != null) {
for (Object template : templateViewer.getCheckedElements()) {
checkedTemplateNames.add((String) template);
}
}
return checkedTemplateNames;
}
@Override
protected void createChildFragments(List<WizardFragment> list) {
Set<String> templateNames = getCheckedTemplateNames();
if (templateNames.isEmpty()) {
return;
}
TemplatePropertiesReader reader = new TemplatePropertiesReader(wc);
for (String templateName : templateNames) {
try {
Set<TemplateProperty> props = reader.read(templateName, new NullProgressMonitor());
if (!props.isEmpty()) {
TcServerTemplateConfigurationFragment page = new TcServerTemplateConfigurationFragment(
templateName, props);
list.add(page);
}
}
catch (CoreException e) {
StatusHandler.log(new Status(IStatus.ERROR, TcServerUiPlugin.PLUGIN_ID,
"Failed to load a template property page for '" + templateName + "'.", e));
}
}
}
}