/*******************************************************************************
* 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.core;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
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.Status;
import org.eclipse.jst.server.tomcat.core.internal.Messages;
import org.eclipse.jst.server.tomcat.core.internal.ProgressUtil;
import org.eclipse.jst.server.tomcat.core.internal.Tomcat60Configuration;
import org.eclipse.jst.server.tomcat.core.internal.TomcatVersionHelper;
import org.eclipse.jst.server.tomcat.core.internal.Trace;
import org.eclipse.jst.server.tomcat.core.internal.WebModule;
import org.eclipse.jst.server.tomcat.core.internal.xml.server40.Connector;
import org.eclipse.jst.server.tomcat.core.internal.xml.server40.Listener;
import org.eclipse.jst.server.tomcat.core.internal.xml.server40.ServerInstance;
import org.eclipse.jst.server.tomcat.core.internal.xml.server40.Service;
import org.eclipse.wst.server.core.IModule;
import org.eclipse.wst.server.core.ServerPort;
/**
* @author Steffen Pingel
* @author Christian Dupuis
*/
public class TcServerConfiguration extends Tomcat60Configuration {
public static final String MODIFY_SERVER_PROPERTY_PROPERTY = "modifyServerProperty";
private static final String FILE_JMXREMOTE_ACCESS = "jmxremote.access";
private static final String FILE_JMXREMOTE_PASSWORD = "jmxremote.password";
private static final String FILE_SPRING_INSIGHT = "spring-insight.yml";
private static final String JMX_SOCKET_LISTENER_CLASS = "com.springsource.tcserver.serviceability.rmi.JmxSocketListener";
private String jmxAccessFile;
private String jmxPasswordFile;
private Properties properties = new Properties();
private String springInsightFile;
private final boolean supportsServlet30;
private final TcServer tcServer;
public TcServerConfiguration(TcServer server, IFolder folder) {
this(server, folder, false);
}
public TcServerConfiguration(TcServer server, IFolder folder, boolean supportsServlet30) {
super(folder);
this.supportsServlet30 = supportsServlet30;
this.tcServer = server;
}
public Listener[] getListeners() {
return serverInstance.getListeners();
}
public List<ServerProperty> getProperties() {
List<ServerProperty> serverProperties = new ArrayList<ServerProperty>();
if (properties != null) {
for (Entry<Object, Object> entry : properties.entrySet()) {
serverProperties.add(new ServerProperty((String) entry.getKey(), (String) entry.getValue()));
}
}
return serverProperties;
}
public ServerInstance getServerInstance() {
return serverInstance;
}
@SuppressWarnings({ "rawtypes" })
@Override
public List getServerPorts() {
List ports = getTcServerPorts();
for (Iterator it = ports.iterator(); it.hasNext();) {
ServerPort port = (ServerPort) it.next();
// remove shutdown port from list to avoid error when starting
// server
if (port.getPort() == -1) {
it.remove();
}
}
return ports;
}
public JmxServicabilityInfo getServicabilityInfo(IPath basePath) {
// add base path property
String path = basePath.toFile().getAbsolutePath();
properties.setProperty("catalina.base", path);
for (Listener listener : getListeners()) {
if (JMX_SOCKET_LISTENER_CLASS.equals(listener.getClassName())) {
return new JmxServicabilityInfo(listener, properties);
}
}
return null;
}
public int getShutdownPort() {
List<TcServerPort> ports = getTcServerPorts();
for (TcServerPort port : ports) {
if ("server".equals(port.getId())) {
return port.getPort();
}
}
return -1;
}
public List<TcServerPort> getTcServerPorts() {
List<TcServerPort> ports = new ArrayList<TcServerPort>();
// first add server port
try {
String portString = server.getPort();
int port = getPort(portString);
TcServerPort serverPort = new TcServerPort("server", Messages.portServer, port, "TCPIP");
serverPort.setPortString(portString);
ports.add(serverPort);
}
catch (Exception e) {
// ignore
}
// add connectors
try {
String instanceServiceName = serverInstance.getService().getName();
int size = server.getServiceCount();
for (int i = 0; i < size; i++) {
Service service = server.getService(i);
int size2 = service.getConnectorCount();
for (int j = 0; j < size2; j++) {
Connector connector = service.getConnector(j);
String name = "HTTP/1.1";
String protocol2 = "HTTP";
boolean advanced = true;
String[] contentTypes = null;
String portString = connector.getPort();
int port = getPort(portString);
String protocol = connector.getProtocol();
if (protocol != null && protocol.length() > 0) {
if (protocol.startsWith("HTTP")) {
name = protocol;
}
else if (protocol.startsWith("AJP")) {
name = protocol;
protocol2 = "AJP";
}
else {
// Get Tomcat equivalent name if protocol handler
// class specified
name = (String) protocolHandlerMap.get(protocol);
if (name != null) {
// Prepare simple protocol string for ServerPort
// protocol
int index = name.indexOf('/');
if (index > 0) {
protocol2 = name.substring(0, index);
}
else {
protocol2 = name;
}
}
// Specified protocol is unknown, just use as is
else {
name = protocol;
protocol2 = protocol;
}
}
}
if (protocol2.toLowerCase().equals("http")) {
contentTypes = new String[] { "web", "webservices" };
}
String secure = connector.getSecure();
if (secure != null && secure.length() > 0) {
name = "SSL";
protocol2 = "SSL";
}
else {
advanced = false;
}
String portId;
if (instanceServiceName != null && instanceServiceName.equals(service.getName())) {
portId = Integer.toString(j);
}
else {
portId = i + "/" + j;
}
TcServerPort serverPort = new TcServerPort(portId, name, port, protocol2, contentTypes, advanced);
serverPort.setPortString(portString);
ports.add(serverPort);
}
}
}
catch (Exception e) {
Trace.trace(Trace.SEVERE, "Error getting server ports", e);
}
return ports;
}
@Override
public void load(IFolder folder, IProgressMonitor monitor) throws CoreException {
super.load(folder, monitor);
properties = new Properties();
if (propertiesFile != null) {
loadProperties(new ByteArrayInputStream(propertiesFile.getBytes()));
}
try {
IFile file = folder.getFile(FILE_JMXREMOTE_ACCESS);
if (file.exists()) {
jmxAccessFile = TomcatVersionHelper.getFileContents(file.getContents());
}
else {
jmxAccessFile = null;
}
file = folder.getFile(FILE_JMXREMOTE_PASSWORD);
if (file.exists()) {
jmxPasswordFile = TomcatVersionHelper.getFileContents(file.getContents());
}
else {
jmxPasswordFile = null;
}
file = folder.getFile(FILE_SPRING_INSIGHT);
if (file.exists()) {
springInsightFile = TomcatVersionHelper.getFileContents(file.getContents());
}
else {
springInsightFile = null;
}
}
catch (Exception e) {
Trace.trace(Trace.WARNING, "Could not load tc Server configuration from: " + folder.getFullPath() + ": "
+ e.getMessage());
}
}
@Override
public void load(IPath path, IProgressMonitor monitor) throws CoreException {
super.load(path, monitor);
properties = new Properties();
if (propertiesFile != null) {
loadProperties(new ByteArrayInputStream(propertiesFile.getBytes()));
}
try {
File file = path.append(FILE_JMXREMOTE_ACCESS).toFile();
if (file.exists()) {
jmxAccessFile = TomcatVersionHelper.getFileContents(new FileInputStream(file));
}
else {
jmxAccessFile = null;
}
file = path.append(FILE_JMXREMOTE_PASSWORD).toFile();
if (file.exists()) {
jmxPasswordFile = TomcatVersionHelper.getFileContents(new FileInputStream(file));
}
else {
jmxPasswordFile = null;
}
file = path.append(FILE_SPRING_INSIGHT).toFile();
if (file.exists()) {
springInsightFile = TomcatVersionHelper.getFileContents(new FileInputStream(file));
}
else {
springInsightFile = null;
}
}
catch (Exception e) {
Trace.trace(Trace.WARNING,
"Could not load tc Server configuration from: " + path.toOSString() + ": " + e.getMessage());
}
}
/**
* Loads catalina.properties to process place holders in configuration
* files.
*/
public void loadProperties(InputStream in) {
// load properties from configuration
try {
properties.load(in);
}
catch (IOException e) {
// ignore
}
}
public void modifyProperty(String key, String value) {
Map<String, String> values = new HashMap<String, String>();
values.put(key, value);
PropertyWriter writer = new PropertyWriter(values);
try {
propertiesFile = writer.apply((propertiesFile != null) ? propertiesFile : "");
}
catch (IOException e) {
Trace.trace(Trace.SEVERE, "Could not read properties", e);
}
properties.put(key, value);
firePropertyChangeEvent(MODIFY_SERVER_PROPERTY_PROPERTY, key, value);
}
public void modifyServerPort(String id, String portString) {
int port = getPort(portString);
try {
if ("server".equals(id)) {
server.setPort(portString);
isServerDirty = true;
firePropertyChangeEvent(MODIFY_PORT_PROPERTY, id, new Integer(port));
return;
}
int i = id.indexOf("/");
// If a connector in the instance Service
if (i < 0) {
int connNum = Integer.parseInt(id);
Connector connector = serverInstance.getConnector(connNum);
if (connector != null) {
connector.setPort(portString);
isServerDirty = true;
firePropertyChangeEvent(MODIFY_PORT_PROPERTY, id, new Integer(port));
}
}
// Else a connector in another Service
else {
int servNum = Integer.parseInt(id.substring(0, i));
int connNum = Integer.parseInt(id.substring(i + 1));
Service service = server.getService(servNum);
Connector connector = service.getConnector(connNum);
connector.setPort(portString);
isServerDirty = true;
firePropertyChangeEvent(MODIFY_PORT_PROPERTY, id, new Integer(port));
}
}
catch (Exception e) {
Trace.trace(Trace.SEVERE, "Error modifying server port " + id, e);
}
}
@Override
public void save(IFolder folder, IProgressMonitor monitor) throws CoreException {
checkModuleReloadState();
super.save(folder, monitor);
try {
// save catalina.properties
if (propertiesFile != null) {
ByteArrayInputStream in = new ByteArrayInputStream(propertiesFile.getBytes());
IFile file = folder.getFile("catalina.properties");
if (file.exists()) {
file.setContents(in, true, true, ProgressUtil.getSubMonitorFor(monitor, 200));
}
else {
file.create(in, true, ProgressUtil.getSubMonitorFor(monitor, 200));
}
}
if (jmxAccessFile != null) {
ByteArrayInputStream in = new ByteArrayInputStream(jmxAccessFile.getBytes());
IFile file = folder.getFile(FILE_JMXREMOTE_ACCESS);
if (!file.exists()) {
file.create(in, true, ProgressUtil.getSubMonitorFor(monitor, 200));
}
}
if (jmxPasswordFile != null) {
ByteArrayInputStream in = new ByteArrayInputStream(jmxPasswordFile.getBytes());
IFile file = folder.getFile(FILE_JMXREMOTE_PASSWORD);
if (!file.exists()) {
file.create(in, true, ProgressUtil.getSubMonitorFor(monitor, 200));
}
}
if (springInsightFile != null) {
ByteArrayInputStream in = new ByteArrayInputStream(springInsightFile.getBytes());
IFile file = folder.getFile(FILE_SPRING_INSIGHT);
if (!file.exists()) {
file.create(in, true, ProgressUtil.getSubMonitorFor(monitor, 200));
}
}
}
catch (Exception e) {
Trace.trace(Trace.SEVERE, "Could not save tc Server configuration to " + folder.toString(), e);
}
}
@Override
public void save(IPath path, IProgressMonitor monitor) throws CoreException {
checkModuleReloadState();
super.save(path, monitor);
}
@SuppressWarnings("unchecked")
private void checkModuleReloadState() {
// Make sure modules are not reloadable if we use JMX or agent-based
// reloading
if (tcServer.isAgentRedeployEnabled() || tcServer.isEnhancedRedeployEnabled()) {
List<WebModule> modules = new ArrayList<WebModule>(getWebModules());
for (int i = 0; i < modules.size(); i++) {
WebModule module = modules.get(i);
if (module.isReloadable()) {
modifyWebModule(i, module.getDocumentBase(), module.getPath(), false);
}
}
}
}
private int getPort(String portString) {
int port = -1;
try {
port = Integer.parseInt(TcServer.substitute(portString, properties));
}
catch (Exception e) {
// ignore
}
return port;
}
@Override
protected IStatus backupAndPublish(IPath tomcatDir, boolean doBackup, IProgressMonitor monitor) {
return super.backupAndPublish(tomcatDir, doBackup, monitor);
}
@Override
protected IStatus cleanupServer(IPath baseDir, IPath installDir, boolean removeKeptContextFiles,
IProgressMonitor monitor) {
return super.cleanupServer(baseDir, installDir, removeKeptContextFiles, monitor);
}
@Override
protected void save(IPath path, boolean forceDirty, IProgressMonitor monitor) throws CoreException {
super.save(path, forceDirty, monitor);
try {
if (jmxAccessFile != null && forceDirty) {
BufferedWriter bw = new BufferedWriter(new FileWriter(path.append(FILE_JMXREMOTE_ACCESS).toFile()));
bw.write(jmxAccessFile);
bw.close();
}
if (jmxPasswordFile != null && forceDirty) {
BufferedWriter bw = new BufferedWriter(new FileWriter(path.append(FILE_JMXREMOTE_PASSWORD).toFile()));
bw.write(jmxPasswordFile);
bw.close();
}
if (springInsightFile != null && forceDirty) {
BufferedWriter bw = new BufferedWriter(new FileWriter(path.append(FILE_SPRING_INSIGHT).toFile()));
bw.write(springInsightFile);
bw.close();
}
}
catch (Exception e) {
Trace.trace(Trace.SEVERE, "Could not save Tomcat configuration to " + path, e);
}
}
// TODO e3.6 remove
protected IStatus updateContextsToServeDirectly(IPath baseDir, String loader, IProgressMonitor monitor) {
try {
// Eclipse 3.6
Method method = TomcatVersionHelper.class.getDeclaredMethod("updateContextsToServeDirectly", IPath.class,
String.class, Boolean.class, IProgressMonitor.class);
return (IStatus) method.invoke(null, baseDir, loader, supportsServlet30, monitor);
}
catch (NoSuchMethodException ignore) {
try {
// Eclipse 3.5
Method method = TomcatVersionHelper.class.getDeclaredMethod("updateContextsToServeDirectly",
IPath.class, String.class, IProgressMonitor.class);
return (IStatus) method.invoke(null, baseDir, loader, monitor);
}
catch (Exception e) {
return new Status(IStatus.ERROR, ITcServerConstants.PLUGIN_ID,
"Internal error while updating contexts", e);
}
}
catch (Exception e) {
return new Status(IStatus.ERROR, ITcServerConstants.PLUGIN_ID, "Internal error while updating contexts", e);
}
}
public ServerPort getMainSslPort() {
Iterator<?> iterator = getServerPorts().iterator();
while (iterator.hasNext()) {
ServerPort port = (ServerPort) iterator.next();
if (port.getProtocol().toLowerCase().equals("ssl") && port.getId().indexOf('/') < 0) {
return port;
}
}
return null;
}
/*
* Make super method package visible.
*/
@Override
protected String getWebModuleURL(IModule webModule) {
return super.getWebModuleURL(webModule);
}
}