/*******************************************************************************
* Copyright (c) 2012 - 2013 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.insight.internal.ui;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
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.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jst.server.tomcat.core.internal.FileUtil;
import org.eclipse.jst.server.tomcat.core.internal.TomcatPlugin;
import org.eclipse.jst.server.tomcat.core.internal.Trace;
import org.eclipse.swt.widgets.Display;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.core.IServerWorkingCopy;
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.TcServerBehaviour;
import com.vmware.vfabric.ide.eclipse.tcserver.internal.core.TcServerCallback;
import com.vmware.vfabric.ide.eclipse.tcserver.reloading.TcServerReloadingPlugin;
/**
* Prompts to enable insight the first time tc Server is launched.
* @author Steffen Pingel
* @author Christian Dupuis
* @author Kris De Volder
* @author Leo Dos Santos
*/
public class InsightTcServerCallback extends TcServerCallback {
public static final String WANT_INSIGHT_DIALOG_TITLE = "Spring Insight";
public final static String ATTRIBUTE_INSIGHT_CONFIGURED = Activator.PLUGIN_ID + ".insightConfigured";
public final static String PREFERENCE_ENABLE_INSIGHT_PONT = "pont.insight.enable";
public final static String WANT_INSIGHT_DIALOG_MESSAGE = "Spring Insight is available for this tc Server instance. Do you want to enable gathering of metrics for this tc Server instance (takes effect on server restart)?\n\nThe setting can be changed in the server editor.";
public InsightTcServerCallback() {
Activator.getDefault().getPreferenceStore()
.setDefault(PREFERENCE_ENABLE_INSIGHT_PONT, MessageDialogWithToggle.PROMPT);
}
private void addInsightToClasspath(TcServer tcServer, ILaunchConfigurationWorkingCopy launchConfiguration)
throws CoreException {
IRuntimeClasspathEntry[] originalClasspath = JavaRuntime.computeUnresolvedRuntimeClasspath(launchConfiguration);
List<IRuntimeClasspathEntry> cp = new ArrayList<IRuntimeClasspathEntry>(Arrays.asList(originalClasspath));
IPath runtimeBaseDirectory = TcServerInsightUtil.getInsightBase(tcServer);
if (runtimeBaseDirectory != null) {
boolean changed = false;
// looking for insight-bootstrap-tomcat-extlibs on tc v2.9,
// looking for insight-bootstrap-tcserver on earlier versions
IPath path = runtimeBaseDirectory.append("bin");
changed |= addJarToClasspath(launchConfiguration, cp, path, "insight-bootstrap", true);
// only add weaver if insight was found
if (changed) {
// tc Server v2.9
if (!addJarToClasspath(launchConfiguration, cp, path, "insight-weaver", false)) {
// fall back for older versions of tc Server
path = runtimeBaseDirectory.append("lib");
addJarToClasspath(launchConfiguration, cp, path, "aspectjweaver", false);
}
List<String> list = new ArrayList<String>(cp.size());
for (IRuntimeClasspathEntry entry : cp) {
try {
list.add(entry.getMemento());
}
catch (Exception e) {
Trace.trace(Trace.SEVERE, "Could not resolve classpath entry: " + entry, e);
}
}
launchConfiguration.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, list);
}
}
}
private boolean addJarToClasspath(ILaunchConfigurationWorkingCopy launchConfiguration,
List<IRuntimeClasspathEntry> cp, IPath path, String prefix, boolean prepend) throws CoreException {
File directory = path.toFile();
String[] filenames = directory.list();
if (filenames != null) {
for (String filename : filenames) {
if (filename.startsWith(prefix) && filename.endsWith(".jar")) {
IRuntimeClasspathEntry entry = JavaRuntime.newArchiveRuntimeClasspathEntry(path.append(filename));
return TcServerBehaviour.mergeClasspathIfRequired(cp, entry, prepend);
}
}
}
return false;
}
private void promptIfInsightNotEnabled(final TcServer tcServer, ILaunchConfigurationWorkingCopy launchConfiguration) {
if (!TcServerInsightUtil.isInsightEnabled(tcServer)) {
String value = Activator.getDefault().getPreferenceStore().getString(PREFERENCE_ENABLE_INSIGHT_PONT);
if (MessageDialogWithToggle.PROMPT.equals(value)) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoCancelQuestion(
UiUtil.getShell(), WANT_INSIGHT_DIALOG_TITLE, WANT_INSIGHT_DIALOG_MESSAGE,
"Do not ask again", false, Activator.getDefault().getPreferenceStore(),
PREFERENCE_ENABLE_INSIGHT_PONT);
if (dialog.getReturnCode() == IDialogConstants.YES_ID) {
IServerWorkingCopy serverWC = tcServer.getServer().createWorkingCopy();
TcServer serverInstance = (TcServer) serverWC.loadAdapter(TcServer.class, null);
new ModifyInsightVmArgsCommand(serverInstance, true).execute();
try {
IServer server = serverWC.save(true, null);
// force publishing to update insight
// directories
// if (server instanceof Server) {
// ((Server)server).setServerPublishState(IServer.PUBLISH_STATE_FULL);
// }
}
catch (CoreException e) {
TomcatPlugin.log(e.getStatus());
}
}
}
});
}
}
}
@Override
public void setDefaults(TcServer server, IProgressMonitor monitor) {
// disable insight by default
new ModifyInsightVmArgsCommand(server, false).execute();
}
@Override
public void setupLaunchConfiguration(final TcServer tcServer, ILaunchConfigurationWorkingCopy launchConfiguration,
IProgressMonitor monitor) throws CoreException {
if (!TcServerInsightUtil.hasInsight(tcServer.getServer())) {
// ignore
return;
}
// prompt user once if insight should be enabled
if (!launchConfiguration.getAttribute(ATTRIBUTE_INSIGHT_CONFIGURED, false)
&& TcServerInsightUtil.isInsightCompatible(tcServer.getServer())) {
launchConfiguration.setAttribute(ATTRIBUTE_INSIGHT_CONFIGURED, true);
promptIfInsightNotEnabled(tcServer, launchConfiguration);
}
// add insight jars to classpath
addInsightToClasspath(tcServer, launchConfiguration);
String agentPath = TcServerInsightUtil.getAgentJarPath(tcServer);
if (TcServerInsightUtil.isInsightEnabled(tcServer)) {
String existingVMArgs = launchConfiguration.getAttribute(
IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, (String) null);
existingVMArgs = appendArg(existingVMArgs, "-Dinsight.devedition", "-Dinsight.devedition=true");
existingVMArgs = appendArg(existingVMArgs, "-Daspectj.overweaving", "-Daspectj.overweaving=true");
existingVMArgs = appendArg(existingVMArgs, "-Djava.awt.headless", "-Djava.awt.headless=true");
existingVMArgs = appendArg(existingVMArgs, "-Dgemfire.disableShutdownHook",
"-Dgemfire.disableShutdownHook=true");
if (agentPath != null) {
existingVMArgs = appendArg(existingVMArgs, "-javaagent:\"" + agentPath + "\"", "-javaagent:\""
+ agentPath + "\"");
// if springloaded agent is enabled, insight-weaver agent must
// be loaded first!!
String reloadingAgent = "-javaagent:\"" + TcServerReloadingPlugin.getAgentJarPath() + "\"";
if (existingVMArgs.contains(reloadingAgent)) {
existingVMArgs = existingVMArgs.replace(reloadingAgent, "");
existingVMArgs = appendArg(existingVMArgs, reloadingAgent, reloadingAgent);
}
}
existingVMArgs = addVMArgs(tcServer, existingVMArgs);
launchConfiguration.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, existingVMArgs);
}
else {
String existingVMArgs = launchConfiguration.getAttribute(
IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, (String) null);
existingVMArgs = existingVMArgs.replace("-Dinsight.devedition=true", "");
if (existingVMArgs != null && existingVMArgs.contains("-Daspectj.overweaving")) {
existingVMArgs = existingVMArgs.replace("-Daspectj.overweaving=true", "");
existingVMArgs = existingVMArgs.replace("-Daspectj.overweaving=false", "");
existingVMArgs = existingVMArgs.replace("-Dgemfire.disableShutdownHook=true", "");
}
if (existingVMArgs != null && agentPath != null) {
existingVMArgs = existingVMArgs.replace("-javaagent:\"" + agentPath + "\"", "");
}
// insight seems to need this even when disabled
existingVMArgs = addInsightBase(tcServer, existingVMArgs);
launchConfiguration.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, existingVMArgs);
}
}
private String appendArg(String existingVMArgs, String arg, String value) {
if (existingVMArgs == null) {
existingVMArgs = value;
}
else {
if (!existingVMArgs.contains(arg)) {
existingVMArgs += " " + value;
}
}
return existingVMArgs;
}
private String addVMArgs(TcServer tcServer, String existingVMArgs) {
existingVMArgs = addInsightBase(tcServer, existingVMArgs);
existingVMArgs = addMemoryArgs(tcServer, existingVMArgs);
return existingVMArgs;
}
private String addMemoryArgs(TcServer tcServer, String existingVMArgs) {
// heap
if (!existingVMArgs.contains("-Xmx")) {
existingVMArgs += " -Xmx1024m";
}
else {
Pattern p = Pattern.compile("-Xmx([0-9]+)m");
Matcher m = p.matcher(existingVMArgs);
if (m.find()) {
try {
int i = Integer.parseInt(m.group(1));
if (i < 1024) {
existingVMArgs = m.replaceFirst("-Xmx1024m");
}
}
catch (NumberFormatException e) {
existingVMArgs = m.replaceFirst("-Xmx1024m");
}
}
}
// stack
if (!existingVMArgs.contains("-XX:MaxPermSize")) {
existingVMArgs += " -XX:MaxPermSize=256m";
}
else {
Pattern p = Pattern.compile("-XX:MaxPermSize=([0-9]+)m");
Matcher m = p.matcher(existingVMArgs);
if (m.find()) {
try {
int i = Integer.parseInt(m.group(1));
if (i < 256) {
existingVMArgs = m.replaceFirst("-XX:MaxPermSize=256m");
}
}
catch (NumberFormatException e) {
existingVMArgs = m.replaceFirst("-XX:MaxPermSize=256m");
}
}
}
return existingVMArgs;
}
private String addInsightBase(TcServer tcServer, String existingVMArgs) {
IPath path = TcServerInsightUtil.getInsightPath(tcServer.getServer());
if (path != null) {
existingVMArgs = addInsightBase(existingVMArgs, path);
}
return existingVMArgs;
}
/**
* Public for testing.
*/
public static String addInsightBase(String existingVMArgs, IPath path) {
String arg = "-Dinsight.base=\"" + path.toOSString() + "\"";
if (!existingVMArgs.contains("-Dinsight.base")) {
existingVMArgs += " " + arg;
}
else {
String regexp = "-Dinsight.base=\".*?\"";
existingVMArgs = existingVMArgs.replaceAll(regexp, Matcher.quoteReplacement(arg));
}
return existingVMArgs;
}
/**
* Mostly for debugging purposes: disable the dialog by presetting the
* "don't ask again" value.
*/
public static void disableDialog(boolean insightEnabled) {
Activator
.getDefault()
.getPreferenceStore()
.setValue(InsightTcServerCallback.PREFERENCE_ENABLE_INSIGHT_PONT,
insightEnabled ? MessageDialogWithToggle.ALWAYS : MessageDialogWithToggle.NEVER);
}
@Override
public void publishServer(TcServer tcServer, int kind, IProgressMonitor monitor) throws CoreException {
if (tcServer.isTestEnvironment()) {
IPath runtimeBase = tcServer.getRuntimeBaseDirectory();
if (runtimeBase == null) {
return;
}
IPath insightBase = tcServer.getInstanceBase(tcServer.getServer().getRuntime());
if (insightBase == null) {
return;
}
IPath destPath = runtimeBase.append("insight");
if (!destPath.toFile().exists()) {
IPath srcPath = insightBase.append("insight");
if (srcPath.toFile().exists()) {
FileUtil.copyDirectory(srcPath.toOSString(), destPath.toOSString(), monitor);
}
}
destPath = runtimeBase.append("webapps").append("insight.war");
if (!destPath.toFile().exists()) {
IPath srcPath = insightBase.append("webapps").append("insight.war");
FileUtil.copyFile(srcPath.toOSString(), destPath.toOSString());
}
}
}
}