REPORT z_test_jco_server.
* Set the RFC destination name. It corresponds to the destination
* defined in SM59.
DATA(rfc_destination) = VALUE rfcdest( ).
rfc_destination = 'MY_JCO_SRV'.
* Define the URL that should be called from the JCo server. So
* to say this is just some data that is handed to the FM.
DATA(url) = |https://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/os/schemas/edm.xsd|. "https://server/path/to/resource|.
DATA(payload) = ||.
DATA(msg) = VALUE char255( ).
* Call the FM remotely and hand the data (URL), but
* also retrieve the result (RESPONSE_PAYLOAD).
CALL FUNCTION 'Z_SAMPLE_ABAP_CONNECTOR_CALL'
DESTINATION rfc_destination
EXPORTING
url = url
IMPORTING
response_payload = payload
EXCEPTIONS
system_failure = 1 MESSAGE msg
communication_failure = 2 MESSAGE msg
internal_failure = 3
timeout = 4
dest_communication_failure = 5
dest_system_failure = 6
update_failure = 7
no_update_authority = 8
OTHERS = 9.
* Check for errors.
IF sy-subrc NE 0.
WRITE: / 'CALL Z_SAMPLE_ABAP_CONNECTOR_CALL SY-SUBRC = ', sy-subrc.
IF msg IS NOT INITIAL.
WRITE: / msg.
ENDIF.
ELSE.
* Display the result. In the exmaple we are expecting that the HTTP-get
* call - done by the JCo server - results in a XML response. We retrieved it from
* the JCo server (via RESPONSE_PAYLOAD) and want to display it here in the ABAP backend.
cl_demo_output=>display_xml( payload ).
ENDIF.
package com.sap;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import org.apache.log4j.Logger;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.ext.ServerDataProvider;
import com.sap.conn.jco.server.DefaultServerHandlerFactory;
import com.sap.conn.jco.server.JCoServer;
import com.sap.conn.jco.server.JCoServerContextInfo;
import com.sap.conn.jco.server.JCoServerErrorListener;
import com.sap.conn.jco.server.JCoServerExceptionListener;
import com.sap.conn.jco.server.JCoServerFactory;
import com.sap.conn.jco.server.JCoServerFunctionHandler;
import com.sap.conn.jco.server.JCoServerState;
import com.sap.conn.jco.server.JCoServerStateChangedListener;
public class SampleAbapConnectorServer implements JCoServerErrorListener, JCoServerExceptionListener, JCoServerStateChangedListener {
private static Logger logger = Logger.getLogger(SampleAbapConnectorServer.class);
/**
* The properties necessary to define the server and destination.
*/
private Properties properties;
private void initialize(String propertiesPath) throws IOException {
InputStream propertiesInputStream = new FileInputStream(propertiesPath);
properties = new Properties();
properties.load(propertiesInputStream);
// Set runtime arguments since some of the JCo-properties need to be passed the the VM
// and simply passing them to JCo won't have any effects.
logger.info("Setting VM argument jco.trace_path to value '"+properties.get("jco.trace_path").toString()+"'");
System.setProperty("jco.trace_path", properties.get("jco.trace_path").toString());
logger.info("Setting VM argument jco.trace_level to value '"+properties.get("jco.trace_level").toString()+"'");
System.setProperty("jco.trace_level", properties.get("jco.trace_level").toString());
logger.info("Setting VM argument jrfc.trace to value '"+properties.get("jrfc.trace").toString()+"'");
System.setProperty("jrfc.trace", properties.get("jrfc.trace").toString());
new MyDestinationDataProvider(properties);
new MyServerDataProvider(properties);
}
/**
* Runnable to listen to the standard input stream to end the server.
*/
private Runnable stdInListener = new Runnable() {
@Override
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
try {
while((line = br.readLine()) != null) {
// Check if the server should be ended.
if(line.equalsIgnoreCase("end")) {
// Stop the server.
server.stop();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
private JCoServer server;
public void serve(String propertiesPath) throws IOException {
initialize(propertiesPath);
try {
server = JCoServerFactory.getServer(properties.getProperty(ServerDataProvider.JCO_PROGID));
} catch(JCoException e) {
throw new RuntimeException("Unable to create the server " + properties.getProperty(ServerDataProvider.JCO_PROGID) + ", because of " + e.getMessage(), e);
}
JCoServerFunctionHandler abapCallHandler = new AbapCallHandler();
DefaultServerHandlerFactory.FunctionHandlerFactory factory = new DefaultServerHandlerFactory.FunctionHandlerFactory();
factory.registerHandler(AbapCallHandler.FUNCTION_NAME, abapCallHandler);
server.setCallHandlerFactory(factory);
// Add listener for errors.
server.addServerErrorListener(this);
// Add listener for exceptions.
server.addServerExceptionListener(this);
// Add server state change listener.
server.addServerStateChangedListener(this);
// Add a stdIn listener.
new Thread(stdInListener).start();
// Start the server
server.start();
logger.info("The program can be stopped typing 'END'");
}
@Override
public void serverExceptionOccurred(JCoServer jcoServer, String connectionId, JCoServerContextInfo arg2, Exception exception) {
logger.error("Exception occured on " + jcoServer.getProgramID() + " connection " + connectionId, exception);
}
@Override
public void serverErrorOccurred(JCoServer jcoServer, String connectionId, JCoServerContextInfo arg2, Error error) {
logger.error("Error occured on " + jcoServer.getProgramID() + " connection " + connectionId, error);
}
@Override
public void serverStateChangeOccurred(JCoServer server, JCoServerState oldState, JCoServerState newState) {
// Defined states are: STARTED, DEAD, ALIVE, STOPPED;
// see JCoServerState class for details.
// Details for connections managed by a server instance
// are available via JCoServerMonitor
logger.info("Server state changed from " + oldState.toString() + " to " + newState.toString() +
" on server with program id " + server.getProgramID());
if(newState.equals(JCoServerState.ALIVE)) {
logger.info("Server with program ID '"+server.getProgramID()+"' is running");
}
if(newState.equals(JCoServerState.STOPPED)) {
logger.info("Exit program");
System.exit(0);
}
}
public static void main(String[] args) throws Exception {
if(args.length == 0) {
logger.error("You must specify a properties file!");
return;
}
new SampleAbapConnectorServer().serve(args[0]);
}
}
# Server
jco.server.connection_count=2
jco.server.gwhost=<host>
jco.server.progid=JCO_SERVER
jco.server.gwserv=<sapgw>
jco.server.repository_destination=does_not_matter
package com.sap;
import java.util.Properties;
import org.apache.log4j.Logger;
import com.sap.conn.jco.ext.Environment;
import com.sap.conn.jco.ext.ServerDataEventListener;
import com.sap.conn.jco.ext.ServerDataProvider;
public class MyServerDataProvider implements ServerDataProvider {
private static Logger logger = Logger.getLogger(MyDestinationDataProvider.class);
/**
* From these properties all necessary destination
* data are gathered.
*/
private Properties properties;
/**
* Initializes this instance with the given {@code properties}.
* Performs a self-registration in case no instance of a
* {@link MyServerDataProvider} is registered so far
* (see {@link #register(MyServerDataProvider)}).
*
* @param properties
* the {@link #properties}
*
*/
public MyServerDataProvider(Properties properties) {
super();
this.properties = properties;
// Try to register this instance (in case there is not already another
// instance registered).
register(this);
}
/**
* Flag that indicates if the method was already called.
*/
private static boolean registered = false;
/**
* Registers the given {@code provider} as server data provider at the
* {@link Environment}.
*
* @param provider
* the server data provider to register
*/
private static void register(MyServerDataProvider provider) {
// Check if a registration has already been performed.
if (registered == false) {
logger.info("There is no " + MyServerDataProvider.class.getSimpleName()
+ " registered so far. Registering a new instance.");
// Register the destination data provider.
Environment.registerServerDataProvider(provider);
registered = true;
}
}
@Override
public Properties getServerProperties(String serverName) {
logger.info("Providing server properties for server '"+serverName+"' using the specified properties");
return properties;
}
@Override
public void setServerDataEventListener(ServerDataEventListener listener) {
}
@Override
public boolean supportsEvents() {
return false;
}
}
package com.sap;
import java.util.Properties;
import org.apache.log4j.Logger;
import com.sap.conn.jco.ext.DestinationDataEventListener;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.Environment;
public class MyDestinationDataProvider implements DestinationDataProvider {
private static Logger logger = Logger.getLogger(MyDestinationDataProvider.class);
/**
* From these properties all necessary destination
* data are gathered.
*/
private Properties properties;
/**
* Initializes this instance with the given {@code properties}.
* Performs a self-registration in case no instance of a
* {@link MyDestinationDataProvider} is registered so far
* (see {@link #register(MyDestinationDataProvider)}).
*
* @param properties
* the {@link #properties}
*
*/
public MyDestinationDataProvider(Properties properties) {
super();
this.properties = properties;
// Try to register this instance (in case there is not already another
// instance registered).
register(this);
}
/**
* Flag that indicates if the method was already called.
*/
private static boolean registered = false;
/**
* Registers the given {@code provider} as destination data provider at the
* {@link Environment}.
*
* @param provider
* the destination data provider to register
*/
private static void register(MyDestinationDataProvider provider) {
// Check if a registration has already been performed.
if (registered == false) {
logger.info("There is no " + MyDestinationDataProvider.class.getSimpleName()
+ " registered so far. Registering a new instance.");
// Register the destination data provider.
Environment.registerDestinationDataProvider(provider);
registered = true;
}
}
@Override
public Properties getDestinationProperties(String destinationName) {
logger.info("Providing destination properties for destination '"+destinationName+"' using the specified properties");
return properties;
}
@Override
public void setDestinationDataEventListener(DestinationDataEventListener listener) {
}
@Override
public boolean supportsEvents() {
return false;
}
}
package com.sap;
import java.io.IOException;
import org.apache.log4j.Logger;
import com.sap.conn.jco.JCoFunction;
import com.sap.conn.jco.server.JCoServerContext;
import com.sap.conn.jco.server.JCoServerFunctionHandler;
public class AbapCallHandler implements JCoServerFunctionHandler {
/**
* This handler only supports one function with name {@code Z_SAMPLE_ABAP_CONNECTOR_CALL}.
*/
public static final String FUNCTION_NAME = "Z_SAMPLE_ABAP_CONNECTOR_CALL";
private static Logger logger = Logger.getLogger(AbapCallHandler.class);
private void printRequestInformation(JCoServerContext serverCtx, JCoFunction function) {
logger.info("----------------------------------------------------------------");
logger.info("call : " + function.getName());
logger.info("ConnectionId : " + serverCtx.getConnectionID());
logger.info("SessionId : " + serverCtx.getSessionID());
logger.info("TID : " + serverCtx.getTID());
logger.info("repository name : " + serverCtx.getRepository().getName());
logger.info("is in transaction : " + serverCtx.isInTransaction());
logger.info("is stateful : " + serverCtx.isStatefulSession());
logger.info("----------------------------------------------------------------");
logger.info("gwhost: " + serverCtx.getServer().getGatewayHost());
logger.info("gwserv: " + serverCtx.getServer().getGatewayService());
logger.info("progid: " + serverCtx.getServer().getProgramID());
logger.info("----------------------------------------------------------------");
logger.info("attributes : ");
logger.info(serverCtx.getConnectionAttributes().toString());
logger.info("----------------------------------------------------------------");
}
public void handleRequest(JCoServerContext serverCtx, JCoFunction function) {
// Check if the called function is the supported one.
if(!function.getName().equals(FUNCTION_NAME)) {
logger.error("Function '"+function.getName()+"' is no supported to be handled!");
return;
}
printRequestInformation(serverCtx, function);
// Get the URL provided from Abap.
String url = function.getImportParameterList().getString("URL");
HttpCaller main = new HttpCaller();
main.initializeSslContext();
main.initializeClient();
String payload = null;
try {
payload = main.invokeGet(url);
} catch(IOException | InterruptedException e) {
// Provide the exception as payload.
payload = e.getMessage();
}
// Provide the payload as exporting parameter.
function.getExportParameterList().setValue("RESPONSE_PAYLOAD", payload);
}
}
package com.sap;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.log4j.Logger;
public class HttpCaller {
public static Logger logger = Logger.getLogger(HttpCaller.class);
private SSLContext context;
private HttpClient client;
public HttpCaller() {
}
public void initializeSslContext() {
// Initialize an SSL context.
try {
/*
* http://stackoverflow.com/questions/6047996/ignore-self-signed-ssl-cert-using-jersey-client
* for
* javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed:
* sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
*/
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager(){
public X509Certificate[] getAcceptedIssuers(){return null;}
public void checkClientTrusted(X509Certificate[] certs, String authType){}
public void checkServerTrusted(X509Certificate[] certs, String authType){}
}};
context = SSLContext.getInstance("TLS");
context.init(null/*keyManagerFactory.getKeyManagers()*/, trustAllCerts, new SecureRandom());
} catch (NoSuchAlgorithmException | KeyManagementException e) {
logger.error(e);
}
}
public void initializeClient() {
// In case you are not using any proxy delete these lines.
// In case you are using a proxy replace and .
/*System.setProperty ("https.proxyHost", "");
System.setProperty ("https.proxyPort", "");
System.setProperty ("http.proxyHost", "");
System.setProperty ("http.proxyPort", "");*/
// Build a client.
client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.ALWAYS)
.version(HttpClient.Version.HTTP_1_1)
.sslContext(context).build();
}
public String invokeGet(String url) throws IOException, InterruptedException {
logger.info("URL: "+url);
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.header("Accept","*/*")
.header("Accept-Language","de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4")
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
return response.body();
}
}
# Root logger option
#log4j.rootLogger=INFO, stdout, file
log4j.rootLogger=DEBUG, stdout
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
#log4j.appender.stdout.layout.ConversionPattern=%-5p [%c{1}:%L]: %m%n
log4j.appender.stdout.layout.ConversionPattern=%m%n
# Server
jco.server.connection_count=2
jco.server.gwhost=<host>
jco.server.progid=JCO_SERVER
jco.server.gwserv=<sapgwXX>
jco.server.repository_destination=does_not_matter
# "Client"
jco.client.lang=en
jco.destination.peak_limit=10
jco.client.client=<client>
jco.client.sysnr=<number>
jco.destination.pool_capacity=3
jco.client.ashost=<host>
jco.client.user=<ABAP backend user>
jco.client.passwd=<ABAP backend password>
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
22 | |
11 | |
10 | |
9 | |
8 | |
6 | |
6 | |
6 | |
5 | |
5 |