/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.kie.workbench.common.screens.datasource.management.backend.integration.wildfly;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.RealmCallback;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.controller.client.OperationBuilder;
import org.jboss.as.controller.client.helpers.ClientConstants;
import org.jboss.dmr.ModelNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.jboss.as.controller.client.helpers.ClientConstants.*;
import static org.kie.workbench.common.screens.datasource.management.util.ServiceUtil.*;
/**
* Base class for Wildfly/EAP based clients.
*/
public abstract class WildflyBaseClient {
private static final Logger logger = LoggerFactory.getLogger( WildflyBaseClient.class );
private static final String PREFIX = "datasource.management.wildfly";
public static final String HTTP_REMOTING_PROTOCOL = "http-remoting";
public static final String HTTPS_REMOTING_PROTOCOL = "https-remoting";
public static final String PROTOCOL = PREFIX + ".protocol";
public static final String HOST = PREFIX + ".host";
public static final String PORT = PREFIX + ".port";
public static final String ADMIN = PREFIX + ".admin";
public static final String PASSWORD = PREFIX + ".password";
public static final String REALM = PREFIX + ".realm";
public static final String PROFILE = PREFIX + ".profile";
public static final String SERVER_GROUP = PREFIX + ".serverGroup";
public static final String REFERENCE_SERVER_HOST = PREFIX + ".referenceServerHost";
public static final String REFERENCE_SERVER_NAME = PREFIX + ".referenceServerName";
public static final String JBOSS_SERVER_NAME = "jboss.server.name";
protected static final String DEFAULT_HOST = "localhost";
protected static final int DEFAULT_PORT = 9990;
protected static final String DEFAULT_ADMIN = null;
protected static final String DEFAULT_ADMIN_PASSWORD = null;
protected static final String DEFAULT_REALM = "ApplicationRealm";
protected String protocol;
protected String host;
protected int port;
protected String admin;
protected String password;
protected String realm;
protected String profile;
protected String serverGroup;
protected String referenceServerHost;
protected String referenceServerName;
public void loadConfig( Properties properties ) {
protocol = getManagedProperty( properties, PROTOCOL, HTTP_REMOTING_PROTOCOL );
host = getManagedProperty( properties, HOST, DEFAULT_HOST );
String currentPort = null;
try {
currentPort = getManagedProperty( properties, PORT, String.valueOf( DEFAULT_PORT ) );
port = Integer.parseInt( currentPort );
} catch ( Exception e ) {
logger.error( "It was not possible to parse port configuration from: " + currentPort +
" default port: " + DEFAULT_PORT + " will be used instead." );
port = DEFAULT_PORT;
}
admin = getManagedProperty( properties, ADMIN, DEFAULT_ADMIN );
password = getManagedProperty( properties, PASSWORD, DEFAULT_ADMIN_PASSWORD );
realm = getManagedProperty( properties, REALM, DEFAULT_REALM );
profile = getManagedProperty( properties, PROFILE, null );
serverGroup = getManagedProperty( properties, SERVER_GROUP, null );
referenceServerHost = getManagedProperty( properties, REFERENCE_SERVER_HOST, null );
referenceServerName = getManagedProperty( properties, REFERENCE_SERVER_NAME, null );
}
public ModelControllerClient createControllerClient( ) throws Exception {
return createControllerClient( true );
}
public ModelControllerClient createControllerClient( boolean checkConnection ) throws Exception {
ModelControllerClient client = ModelControllerClient.Factory.create( protocol, InetAddress.getByName( host ), port,
new CallbackHandler( ) {
public void handle( Callback[] callbacks )
throws IOException, UnsupportedCallbackException {
for ( Callback current : callbacks ) {
if ( current instanceof NameCallback ) {
NameCallback ncb = ( NameCallback ) current;
ncb.setName( admin );
} else if ( current instanceof PasswordCallback ) {
PasswordCallback pcb = ( PasswordCallback ) current;
pcb.setPassword( password.toCharArray( ) );
} else if ( current instanceof RealmCallback ) {
RealmCallback rcb = ( RealmCallback ) current;
rcb.setText( realm );
} else {
throw new UnsupportedCallbackException( current );
}
}
}
} );
if ( checkConnection ) {
testConnection( client );
}
return client;
}
/**
* Tests the connection with the server.
* @return If the connection was successfully established returns a String with the server version information.
* @throws Exception If it was not possible to connect to server.
*/
public String testConnection( ) throws Exception {
ModelControllerClient client = null;
try {
client = createControllerClient( false );
return testConnection( client );
} finally {
safeClose( client );
}
}
private String testConnection( final ModelControllerClient client ) throws Exception {
try {
final ModelNode op = new ModelNode( );
op.get( ClientConstants.OP ).set( "read-resource" );
final ModelNode returnVal = client.execute( new OperationBuilder( op ).build( ) );
final String productName = returnVal.get( "result" ).get( "product-name" ).asString( );
final String productVersion = returnVal.get( "result" ).get( "product-version" ).asString( );
final String releaseVersion = returnVal.get( "result" ).get( "release-version" ).asString( );
final String releaseCodeName = returnVal.get( "result" ).get( "release-codename" ).asString( );
final StringBuilder stringBuilder = new StringBuilder( );
stringBuilder.append( productName + ", " + productVersion );
stringBuilder.append( " (" + releaseCodeName + ", " + releaseVersion + ")" );
return stringBuilder.toString( );
} catch ( Exception e ) {
logger.error( "It was not possible to open connection to Wildfly/EAP server.", e );
throw new Exception( "It was not possible to open connection to server. " + e.getMessage( ) );
}
}
/**
* Checks the outcome returned by server when an operation was executed.
*
* @param response ModelNode returned by server as response.
*
* @throws Exception
*/
public void checkResponse( ModelNode response ) throws Exception {
final String outcome = response.get( OUTCOME ) != null ? response.get( OUTCOME ).asString() : "";
if ( outcome.contains( "failed" ) ) {
throw new Exception( "operation execution failed. :" + getErrorDescription( response ) );
} else if ( outcome.contains( "canceled" ) ) {
throw new Exception( "operation execution was canceled by server: " + getErrorDescription( response ) );
} else if ( outcome.contains( SUCCESS ) ) {
//great!!!
}
}
public boolean isFailure( ModelNode response ) {
final String outcome = response.get( OUTCOME ) != null ? response.get( OUTCOME ).asString() : "";
return outcome.contains( "failed" );
}
public void safeClose( final Closeable closeable ) {
if ( closeable != null ) {
try {
if ( logger.isDebugEnabled() ) {
logger.debug( "starting ModelControllerClient connection close" );
}
boolean disableClose = Boolean.valueOf( System.getProperty( "disableClose" ) );
if ( disableClose ) {
logger.warn( "ModelControllerClient connection closing was disabled" );
} else {
closeable.close();
}
if ( logger.isDebugEnabled() ) {
logger.debug( "ModelControllerClient connection was closed successfully" );
}
} catch ( Exception e ) {
logger.error( "An error was produced during ModelControllerClient closing: ", e );
}
}
}
public boolean isStandalone( ) {
return profile == null;
}
public String getReferenceServerName( ) {
if ( referenceServerName == null ) {
referenceServerName = System.getProperty( JBOSS_SERVER_NAME );
}
return referenceServerName;
}
public String getReferenceServerHost( ) {
if ( referenceServerHost == null ) {
String currentServer = getReferenceServerName( );
try {
List< String > hosts = getHosts( );
for ( String host : hosts ) {
List< String > servers = getServers( host );
if ( servers.contains( currentServer ) ) {
referenceServerHost = host;
break;
}
}
} catch ( Exception e ) {
logger.error( "It was not possible to calculate the referenceServerHost for currentServer: "
+ currentServer, e );
}
}
return referenceServerHost;
}
public List< String > getHosts( ) throws Exception {
ArrayList< String > result = new ArrayList<>( );
ModelControllerClient client = null;
ModelNode response = null;
try {
// /:read-children-names(child-type=host)
ModelNode operation = new ModelNode( );
operation.get( OP ).set( "read-children-names" );
operation.get( "child-type" ).set( "host" );
client = createControllerClient( );
response = client.execute( operation );
if ( !isFailure( response ) ) {
if ( response.hasDefined( RESULT ) ) {
List< ModelNode > nodes = response.get( RESULT ).asList( );
for ( ModelNode node : nodes ) {
result.add( node.asString( ) );
}
}
}
} finally {
safeClose( client );
checkResponse( response );
}
return result;
}
public List< String > getServers( String host ) throws Exception {
ArrayList< String > result = new ArrayList<>( );
ModelControllerClient client = null;
ModelNode response = null;
try {
// /host=host1:read-children-names(child-type=server)
ModelNode operation = new ModelNode( );
operation.get( OP ).set( "read-children-names" );
operation.get( "child-type" ).set( "server" );
operation.get( OP_ADDR ).add( "host", host );
client = createControllerClient( );
response = client.execute( operation );
if ( !isFailure( response ) ) {
if ( response.hasDefined( RESULT ) ) {
List< ModelNode > nodes = response.get( RESULT ).asList( );
for ( ModelNode node : nodes ) {
result.add( node.asString( ) );
}
}
}
} finally {
safeClose( client );
checkResponse( response );
}
return result;
}
private String getErrorDescription( ModelNode response ) {
if ( response.hasDefined( FAILURE_DESCRIPTION ) ) {
return response.get( FAILURE_DESCRIPTION ).asString();
} else {
return response.asString();
}
}
}