/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.runtime;
import static org.teiid.designer.runtime.DqpPlugin.PLUGIN_ID;
import static org.teiid.designer.runtime.DqpPlugin.Util;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.wst.server.core.IServer;
import org.eclipse.wst.server.core.IServerLifecycleListener;
import org.eclipse.wst.server.core.IServerListener;
import org.eclipse.wst.server.core.ServerEvent;
import org.jboss.ide.eclipse.as.core.server.internal.v7.JBoss7Server;
import org.jboss.ide.eclipse.as.management.core.JBoss7ManangerException;
import org.teiid.core.designer.util.StringUtilities;
import org.teiid.designer.runtime.TeiidServerFactory.ServerOptions;
import org.teiid.designer.runtime.adapter.JBoss7ServerUtil;
import org.teiid.designer.runtime.adapter.TeiidServerAdapterFactory;
import org.teiid.designer.runtime.spi.ITeiidServer;
import org.teiid.designer.runtime.spi.ITeiidServerManager;
import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version;
/**
* Singleton listener for monitoring both the {@link IServer}s'
* life-cycle and their state.
*/
public class TeiidParentServerListener implements IServerLifecycleListener, IServerListener {
private static TeiidParentServerListener instance;
/*
* The following exception is being caught during server start.
* Could not execute "read-children-names" for undefined. Failure was "JBAS013493: System boot is in process; execution of remote management operations is not currently available".
*/
public static String JBAS013493_CODE = "JBAS013493"; //$NON-NLS-1$
/**
* Get the singleton instance of of this class
*
* @return instance
*/
public static TeiidParentServerListener getInstance() {
if (instance == null)
instance = new TeiidParentServerListener();
return instance;
}
private TeiidServerAdapterFactory factory = new TeiidServerAdapterFactory();
private boolean sleep;
private Thread startTeiidServerThread = null;
private Set<IServerListener> registeredParentListeners = new HashSet<IServerListener>();
private TeiidParentServerListener() {}
@Override
public void serverAdded(IServer server) {
if (sleep) return;
// Initialise the Teiid Instance manager is not already initialised
DqpPlugin.getInstance().getServerManager();
server.addServerListener(this);
// New server added so add a teiid instance, even though it is not currently connected
try {
factory.adaptServer(server, ServerOptions.NO_CHECK_CONNECTION, ServerOptions.ADD_TO_REGISTRY);
} catch (final Exception ex) {
if(! ex.getMessage().contains(TeiidParentServerListener.JBAS013493_CODE)) {
DqpPlugin.handleException(ex);
}
}
}
@Override
public void serverChanged(IServer server) {
if (sleep) return;
ITeiidServerManager serverManager = DqpPlugin.getInstance().getServerManager();
try {
for (ITeiidServer teiidServer : serverManager.getServers()) {
if (! server.equals(teiidServer.getParent()))
continue;
/*
* Cannot use updateServer as it replaces rather than modifies the existing server
* and references in editor will thus hang on to the old defunct version.
*
* Only update the settings which may have been queried from the server.
*
* The admin settings were changed in version 8+ to use the admin connection
* of the jboss parent server. Thus, version 7 should not try and change these
* while version 8+ should.
*/
if (teiidServer.getServerVersion().isGreaterThan(Version.TEIID_7_7.get())) {
teiidServer.getTeiidAdminInfo().setAll(
teiidServer.getTeiidAdminInfo().getHost(),
teiidServer.getTeiidAdminInfo().getPort(),
teiidServer.getTeiidAdminInfo().getUsername(),
teiidServer.getTeiidAdminInfo().getPassword(),
teiidServer.getTeiidAdminInfo().isSecure());
}
String portNumber = serverManager.getJdbcPort(teiidServer, true);
if( StringUtilities.isEmpty(portNumber) ) {
JBoss7Server jb7 = (JBoss7Server) server.loadAdapter(JBoss7Server.class, null);
if (jb7 != null) {
try {
portNumber = JBoss7ServerUtil.getJdbcPort(server, jb7);
teiidServer.getTeiidJdbcInfo().setPort(portNumber);
} catch (JBoss7ManangerException e) {
if( e.getMessage().contains("teiid-jdbc") ) {
// do nothing... the server does not have Teiid Installed
} else {
DqpPlugin.handleException(e);
}
}
}
}
// Server config may have changed (admin port or port offset)
int teiidPort = teiidServer.getTeiidAdminInfo().getPortNumber();
JBoss7Server jb7 = (JBoss7Server) server.loadAdapter(JBoss7Server.class, null);
if( jb7 != null ) {
int managementPort = jb7.getManagementPort();
if( teiidPort != managementPort ) {
teiidServer.getTeiidAdminInfo().setPort(Integer.toString(managementPort));
}
}
teiidServer.notifyRefresh();
return;
}
/*
* We have a parent server with no Teiid Instance attached
* This may be intentional if the parent server is not teiid
* enabled but should check just in case.
*/
factory.adaptServer(server, ServerOptions.ADD_TO_REGISTRY);
} catch (Exception ex) {
if(! ex.getMessage().contains(TeiidParentServerListener.JBAS013493_CODE)) {
DqpPlugin.handleException(ex);
}
}
}
@Override
public void serverRemoved(IServer server) {
if (sleep) return;
server.removeServerListener(this);
ITeiidServerManager serverManager = DqpPlugin.getInstance().getServerManager();
// Tidy up the server manager by removing the related Teiid Instance
for (ITeiidServer teiidServer : serverManager.getServers()) {
if (server.equals(teiidServer.getParent())) {
serverManager.removeServer(teiidServer);
break;
}
}
}
@Override
public void serverChanged(ServerEvent event) {
if (sleep) return;
if (event == null) return;
int eventKind = event.getKind();
if ((eventKind & ServerEvent.SERVER_CHANGE) == 0) return;
// server change event
if ((eventKind & ServerEvent.STATE_CHANGE) == 0) return;
int state = event.getState();
IServer parentServer = event.getServer();
try {
if (state == IServer.STATE_STOPPING || state == IServer.STATE_STOPPED) {
ITeiidServer teiidServer = factory.adaptServer(parentServer);
for( IServerListener listener: registeredParentListeners ) {
listener.serverChanged(event);
}
if( teiidServer == null ) {
return;
}
teiidServer.disconnect();
// Server config may have changed (admin port or port offset)
int teiidPort = teiidServer.getTeiidAdminInfo().getPortNumber();
JBoss7Server jb7 = (JBoss7Server) parentServer.loadAdapter(JBoss7Server.class, null);
if( jb7 != null ) {
int managementPort = jb7.getManagementPort();
if( teiidPort != managementPort ) {
teiidServer.getTeiidAdminInfo().setPort(Integer.toString(managementPort));
}
}
} else if (state == IServer.STATE_STARTED) {
teiidServerStarted(parentServer);
for( IServerListener listener: registeredParentListeners ) {
listener.serverChanged(event);
}
}
} catch (Exception ex) {
if(! ex.getMessage().contains(TeiidParentServerListener.JBAS013493_CODE)) {
DqpPlugin.handleException(ex);
}
}
}
/**
* @param parentServer
* @throws Exception
*/
private void teiidServerStarted(final IServer parentServer) {
if (startTeiidServerThread != null && startTeiidServerThread.isAlive())
return;
Runnable serverStartRunnable = new Runnable() {
@Override
public void run() {
try {
tryConnecting(parentServer);
} catch (Exception ex) {
if( ! ex.getMessage().contains(TeiidParentServerListener.JBAS013493_CODE)) {
DqpPlugin.handleException(ex);
}
}
}
/**
* @param parentServer
* @throws Exception
*/
private void tryConnecting(final IServer parentServer) throws Exception {
// Default wait time == 120 seconds (2 minutes)
// Calculate the # attempts to based on a 4 second wait time
int timeOutTotal = getTimeoutPrefSecs();
int waitTimeInMS = 4000; // 4 seconds
int calcAttempts = timeOutTotal/4;
int nAttempts = calcAttempts == 0 ? 10 : calcAttempts;
ITeiidServer teiidServer = factory.adaptServer(parentServer, ServerOptions.ADD_TO_REGISTRY);
if (teiidServer != null) {
// Places the teiid server is a connecting state which
// can be detected by the UI
teiidServer.startConnecting();
}
boolean parentConnected = false;
/*
* Update all the settings since the server has been started and a
* proper set of queries can take place.
*/
ITeiidServer queryServer = null;
int attempts = 0;
// Loop will try to connect the teiid server 10 times after receiving a server
// start signal from the server framework. Given the thread sleeps for 5 seconds
// then the server has 60 seconds to finish starting if not already started.
Exception logThisException = null;
while ( (!parentConnected || queryServer == null ) && attempts < nAttempts) {
try {
attempts++;
parentConnected = teiidServer != null && teiidServer.isParentConnected() && adaptServerOK(parentServer);
if( parentConnected ) {
queryServer = factory.adaptServer(parentServer, ServerOptions.NO_CHECK_SERVER_REGISTRY);
}
Thread.sleep(waitTimeInMS);
} catch (Exception ex) {
logThisException = ex;
}
}
if( queryServer != null ) {
/*
* Updates those settings that may have been successfully queried from the
* contacted server.
*/
teiidServer.getTeiidAdminInfo().setAll(queryServer.getTeiidAdminInfo());
teiidServer.getTeiidJdbcInfo().setPort(queryServer.getTeiidJdbcInfo().getPort());
teiidServer.reconnect();
// Cache the default Teiid JDBC port that was discovered on connection
try {
int defaultPort = Integer.parseInt(queryServer.getTeiidJdbcInfo().getPort());
DqpPlugin.getInstance().getServerManager().setJdbcPort(teiidServer, defaultPort, false);
// If there is no override port cached, set it the same value as the default so they start in sync
if( DqpPlugin.getInstance().getServerManager().getJdbcPort(teiidServer, true) == null ) {
DqpPlugin.getInstance().getServerManager().setJdbcPort(teiidServer, defaultPort, true);
}
} catch (Exception e) {
// DO NOTHING
}
} else if( teiidServer != null ) {
// If the query server is null then this is not a Teiid-enabled JBoss Server but
// a TeiidServer was cached in the registry, presumably due to an adaption
// being made while the server was not started. Since we now know better, we
// can correct the registry.
DqpPlugin.getInstance().getServerManager().removeServer(teiidServer);
if( logThisException != null ) {
DqpPlugin.handleException(logThisException);
}
return;
} else {
IStatus status = new Status(IStatus.WARNING, PLUGIN_ID,
Util.getString("warningServerNotFullyStarted_RefreshServer", parentServer.getName())); //$NON-NLS-1$
Util.log(status);
}
return;
}
};
startTeiidServerThread = new Thread(serverStartRunnable, "Teiid Server Starting Thread"); //$NON-NLS-1$
startTeiidServerThread.start();
}
public void addRegisteredParentListener(IServerListener listener) {
registeredParentListeners.add(listener);
}
public void removeRegisteredParentListener(IServerListener listener) {
if (registeredParentListeners == null)
return;
registeredParentListeners.remove(listener);
}
private int getTimeoutPrefSecs() {
return DqpPlugin.getInstance().getPreferences().getInt(PreferenceConstants.TEIID_SERVER_STARTUP_TIMEOUT_SEC, PreferenceConstants.TEIID_SERVER_STARTUP_TIMEOUT_SEC_DEFAULT);
}
private boolean adaptServerOK(final IServer parentServer) throws Exception {
try {
factory.adaptServer(parentServer, ServerOptions.NO_CHECK_SERVER_REGISTRY);
} catch (JBoss7ManangerException e) {
if( e.getMessage().contains(JBAS013493_CODE)) {
return false;
}
throw new Exception(e);
} catch (Exception e) {
throw new Exception(e);
}
return true;
}
/**
* Deafen this listener
*/
public void sleep() {
sleep = true;
}
/**
* Awaken this listener
*/
public void wake() {
sleep = false;
}
}