1 2 3 4 Previous Next

Java Development

50 Posts

Based on the learning from An example of building Java project using Maven we can now use Maven to do more practical task.


I plan to create a Hello World application based on Spring framework. Instead of manually downloading Spring framework jar files and configured the usage of those jars into my Java project, I can now leverage Maven to make the whole process run automatically.

 

Install m2e - Maven integration plugin for Eclipse:

clipboard1.png

And then create a new Java project, and you can easily convert this project to a Maven project via context menu:

clipboard2.png

Once converted, you can then declare the dependency by clicking pom.xml and choose "Maven->Add Dependency" from context menu:

clipboard3.png

Enter group id, artifact id and version accordingly. Once done, the XML content should look as below:

 

<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>com.sap.MavenSandbox</groupId>  
   <artifactId>MavenSandbox</artifactId>  
   <version>0.0.1-SNAPSHOT</version>  
   <dependencies>  
          <dependency>  
               <groupId>junit</groupId>  
               <artifactId>junit</artifactId>  
               <version>4.10</version>  
               <scope>test</scope>  
          </dependency>  
          <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
           <version>4.2.6.RELEASE</version>
           </dependency>
   </dependencies>  
</project>

 

Trigger a build via mvn clean install, and Maven will automatically download the necessary jars of Spring framework and store them to .m2 folder.

clipboard4.png

Now we can start programming in Spring.

My project has the following hierarchy:

clipboard5.png

All missing import could be correctly parsed and easily fixed now.

clipboard6.png

If you would like to do some debugging on Spring framework source code, you can also download the related source code very easily via "Maven->Download Sources".

clipboard7.png

After that you could just set a breakpoint on the constructor of HelloWorld class and then study how Spring instantiates the instance of this class configured in beans.xml via reflection:

clipboard8.png

The source code of files used in the project

 

HelloWorld.java

 

package main.java.com.sap;
public class HelloWorld {
    private String message;
    public void setMessage(String message){
       this.message  = message;
    }
    public HelloWorld(){
     System.out.println("in constructor");
    }
  
    public void getMessage(){
       System.out.println("Your Message : " + message);
    }
}

 

MavenSandbox.java

 

package main.java.com.sap;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MavenSandbox {
public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
  HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
     obj.getMessage();
}
public String hello(){
  return "Hello world";
}
}

 

MavenSandboxTest.java

 

package test.java.com.sap;
import static org.junit.Assert.assertEquals;
import main.java.com.sap.MavenSandbox;
import org.junit.Test;
public class MavenSandboxTest {
@Test 
public void test() {
  MavenSandbox tool = new MavenSandbox();
  assertEquals(tool.hello(),"Hello world");
}
}

 

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <bean id="helloWorld" class="main.java.com.sap.HelloWorld">
       <property name="message" value="Hello World!"/>
   </bean>
</beans>

Prerequisite: download and configure Maven in your laptop. Once done, type "mvn" in command line and you should observe the following output by Maven:

clipboard1.png

Suppose I have a simple Java project with following package hierarchy:


clipboard2.png

The source code of MavenSandboxTest is also very simple:

 

package test.java.com.sap;
import static org.junit.Assert.assertEquals;
import main.java.com.sap.MavenSandbox;
import org.junit.Test;
public class MavenSandboxTest {
@Test 
public void test() {
  MavenSandbox tool = new MavenSandbox();
  assertEquals(tool.hello(),"Hello world");
}
}

How to build this simple Java project using Maven?

 

Create a pom.xml under the root folder of your project,

clipboard3.png

and paste the following source code:

 

<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>com.sap.MavenSandbox</groupId>  
   <artifactId>MavenSandbox</artifactId>  
   <version>0.0.1-SNAPSHOT</version>  
   <dependencies>  
          <dependency>  
               <groupId>junit</groupId>  
               <artifactId>junit</artifactId>  
               <version>4.10</version>  
               <scope>test</scope>  
          </dependency>  
   </dependencies>  
</project> 

 

Create a new Configuration:

clipboard4.png


Specify the following settings and click run:

clipboard5.png

If everything goes well, you should see the Build Success message:

clipboard6.png

and there will be a new folder "target" generated automatically:

clipboard7.png

go to folder classes, and you can execute the compiled java class via command "java main.java.com.sap.MavenSandbox":

clipboard8.png

or you can also directly execute the jar file via the command below ( you should first navigate back to target folder )

clipboard9.png

since we have specified the dependency of JUnit with version 4.10 in pom.xml:

clipboard10.png

so when "mvn clean install" is executed, you can observe the corresponding jar file is automatically downloaded by Maven:

clipboard11.png

Finally you could find the downloaded jar file from this folder:

clipboard12.png

1. start Jetty server under debug mode via mvn jetty:run

clipboard1.png

2. In Eclipse, create a new Debug configuration->Remote Java Application

clipboard2.png

Specify Host as localhost and port 8000:

clipboard3.png

Click debug button:

clipboard4.png

You should observe that the Jetty server listening to port 8000 has accepted this debug request and start application via localhost:8080 now:

clipboard5.png

3. go to localhost:8080, perform action to trigger the breakpoint:

clipboard6.png

And now in Eclipse, breakpoint is triggered!

clipboard7.png

Note: if you meet with error message "bind error, address already in use", please first use "netstat -lp" to find the process id which occupies the address and kill that process by kill -9 <pid>.

clipboard8.png

clipboard9.png

If still does not work, restart the virtual machine instance.

Jerry Wang

Using Optional in Java 8

Posted by Jerry Wang Dec 15, 2015

Optional class is available in Java8. Let's use some examples to understand its logic.

 

Example 1

 

Optional instance acts as a wrapper for your productive Java class. For example I have a Person class:

 

class Person {
  private String mName;
  public Person(String name) {
   mName = name;
  }
  public void greet() {
   System.out.println("I am: " + mName);
  }
}

line 21 will trigger an null pointer exception.

clipboard1.png

Console will print out "Error: java.lang.NullPointerException - null". To avoid it we can use method ofNullable and evaluate the availability using method isPresent. The following two lines 28 and 32 will print false and true accordingly.

clipboard2.png

Example 2

 

Instead of writing code like:

if ( person != null ) {
     person.greet();
}

to avoid null pointer exception before Java 8, now we can write:

clipboard3.png

The lambda expression specified within method ifPresent will only be executed if the method isPresent() returns true.

 

Example 3

 

Now we can avoid the ( condition ) ? x: y statement using orElse method of Optional:

clipboard4.png

clipboard5.png

Example 4

 

We need to figure out if a person is Jerry or not. The old style has to always check whether the reference jerry is available or not.

clipboard6.png

The new approach is to use filter method of instance of class Optional<Person>, thus the null check could be avoided (replaced by ifPresent ).

 

old style-> Jerry found: Jerry
new style-> Jerry found: Jerry

 

The enhanced version using map function:

clipboard7.png

Difference between method map and flatMap

 

map accepts a function returning the unwrapped data without Optional ( type U ), and the flatMap accepts a function returning the wrapped data with Optional ( type Optional<U> ).

clipboard8.png

Recently I am trying to find a handy tool to measure the performance of my Java application and finally I think the VisualVM provided by JDK is the ideal one. This blog is written based on JDK1.7 + Eclipse 4.3.2.

 

What is VisualVM

 

It is a tool automatically available after JDK is installed. The executable file could be found on your <JDK installation folder>/bin as displayed below.

 

clipboard1.png

In order to measure the performance of your application, it is necessary for the application to be recognized by VisualVM first.

There is a plugin named "VisualVM launcher for Eclipse" which can help us about it.

 

Install and configure VisualVM launcher

 

1. download the zip from http://visualvm.java.net/eclipse-launcher.html. Unzip the file and put it to the plugin folder of your Eclipse installation folder. In my laptop it looks like below. There should be a site.xml inside the unzipped folder.

clipboard2.png

2. In Eclipse, choose menu "Help->Install New Software", click "Local", add locate the folder to the one you finish in step1.

clipboard3.png

Then the local downloaded plugin is successfully parsed and ready for install.

clipboard4.png

finish the installation.


clipboard5.png

3. Restart Eclipse, then you can find a new option via the path below. Configure two paths accordingly.

clipboard6.png

For "JDK Home", if you configure the JRE path by mistake, later when you try to measure your application, the VisualVM will fail to load with the following error message:

clipboard7.png

Now the plugin is ready to use.

 

Do performance measurement

 

1. Select your Java project, choose context menu "Run as"->"Run configuration", create a new Application configuration by specifying VisualVM launcher as its launcher, instead of the default Eclipse JDT launcher.

clipboard8.png

2. For example I have a Java application which sorts an array by QuickSort algorithm written by myself and I would like to get its performance data, then I set a breakpoint on line 57, before my main logic is executed.

 

Then launch the application in debugging mode with the application configuration created in previous step.

 

Afterwards VisualVM will automatically be launched and successfully recognize the execution of my application.

 

Click Profiler tab:

clipboard9.png

Current status: profiling inactive. Click CPU button:

clipboard10.png


Now profiling is activated:

clipboard11.png

3. Go back to Eclipse and click F8 to finish execution. Once finished, VisualVM will immediately capture this event and notify you. Just click Yes to get performance result.

clipboard12.png

The result is displayed as below:

clipboard13.png

Today I found a useful free software called "SourceMonitor" which can help to calculate and monitor the java code ( and other programming language like C++, C# etc ) complexity.

 

clipboard18.png

 

For the definition and how to calculate cyclomatic complexity itself, please refer to detail in wikipedia.

 

In order to demonstrate the usage of this software, I use a very simple java class below for example:

 

package test;
import java.util.ArrayList;
public class monthTool {
 static ArrayList<String> monthCollection = new ArrayList<String>();
 public static void main(String[] args) {
  monthTool tool = new monthTool();
  tool.printV1(1);
  tool.printV2(2);
  tool.printV1(0);
  tool.printV2(-1);
  tool.printV3(3);
  tool.printV3(13);
 }
 public monthTool(){
  monthCollection.add("Invalid");
  monthCollection.add("January");
  monthCollection.add("Febrary");
  monthCollection.add("March");
  monthCollection.add("April");
  monthCollection.add("May");
  monthCollection.add("June");
  monthCollection.add("July");
  monthCollection.add("August");
  monthCollection.add("September");
  monthCollection.add("October");
  monthCollection.add("November");
  monthCollection.add("December");
 }
 public void printV1(int month){
  System.out.println("Month is: " + getMonthNameV1(month));
 }
 public void printV2(int month){
  if( month >= 1 && month <= 12)
   System.out.println("Month is: " + getMonthNameV2(month));
  else
   System.out.println("Please specify a valid month");
 }
 public void printV3(int month) {
  System.out.println("Month is: " + getMonthNameV3(month));
 }
 public String getMonthNameV2(int month){
  if( month == 1)
   return "January";
  else if( month == 2)
   return "Febrary";
  else if( month == 3)
   return "March";
  else if( month == 4)
   return "April";
  else if( month == 5)
   return "May";
  else if( month == 6)
   return "June";
  else if( month == 7)
   return "July";
  else if( month == 8)
   return "August";
  else if( month == 9)
   return "September";
  else if( month == 10)
   return "October";
  else if( month == 11)
   return "November";
  else if( month == 12)
   return "December";
  else
   return "Invalid";
 }
 public String getMonthNameV1(int month){
  switch (month){
  case 1:
   return "January";
  case 2:
   return "Febrary";
  case 3:
   return "March";
  case 4:
   return "April";
  case 5:
   return "May";
  case 6:
   return "June";
  case 7:
   return "July";
  case 8:
   return "August";
  case 9:
   return "September";
  case 10:
   return "October";
  case 11:
   return "November";
  case 12:
   return "December";
  default:
   return "Invalid";
  }
 }
 public String getMonthNameV3(int month){
  try {
   return monthCollection.get(month);
  }
  catch (java.lang.IndexOutOfBoundsException e){
   return "Invalid";
  }
 }
}

It has three different ways to convert an integer into a month name if possible, or else the string "Invalid" is returned.

 

1. Create a new project:

clipboard1.png


Here you could find out all suppored programming language:


clipboard2.png

2. specify a project name and locate the directory of SourceMonitor project file. For me I choose to store it into the same path of my test java project.

clipboard3.png

3. Specify which source files will be scaned by SourceMonitor:

clipboard4.png

4. For the left steps in wizard, just use default settings and finish wizard. Click OK button to start scan.

clipboard5.png

And soon we get the analysis result. Since we are more interested with the detail of each method, we choose "Display Method Metrics" from context menu.

clipboard6.png

From the result list it is easily known that the third approach of month name retrieval is much better than the first two ones, no matter regarding complexity or statement number.

clipboard7.png

You could also view the result list via "Chart Method Metrics" from context menu:

clipboard8.png

Take the complex graph for example: X axis is the complexity value of each method, the Y axis is the total occurance of each different complex value.


clipboard9.png

The purpose of this document is to provide best practices in order to create useful Logging and Tracing messages.

1.    Definitions

1.1  Logs

Logs are targeting administrators when they encounter some problem. Therefore logs should describe the problem in an easily understandable form. Avoid using internal terms and abbreviations in log messages. Checking component versions
Example:
The network connection has been broken
Use com.sap.tc.logging.Category to log information, warnings or errors to an administrator of a customer system.

1.2  Trace Messages

Traces are meant for developers or support engineers trying to identify a problem. They should be as detailed as possible. There is no performance impact of having additional trace statements, since traces can be switched on or off based on severity. The goal: provide nearly the same amount of information what we can collect with debugging.
Example:
New change request cannot be created when there is one with status CR_CREATED, CR_INPROCESS, CR_ACCEPTED for the same sales order id and item id
Use com.sap.tc.logging.Location to emit trace messages.

2.    Implementation

1.3  Logging

In order to perform logging, the following steps should be followed:
1: Retrieve the Category Object for logging
private static final Category category = Category.getCategory(Category.APPLICATIONS, "<APPLICATION NAME>")
For convenience and to avoid unnecessary object creation, the following static variables are defined:
  • -          com.sap.tc.logging.Category.SYS_DATABASE
  • -          com.sap.tc.logging.Category.SYS_NETWORK
  • -          com.sap.tc.logging.Category.SYS_SERVER
  • -          com.sap.tc.logging.Category.SYS_SECURITY
  • -          com.sap.tc.logging.Category.APPLICATIONS
  • -          com.sap.tc.logging.Category.SYS_USER_INTERFACE
Security related logging should be performed using Category.APPS_COMMON_SECURITY
2: Log the message using the one of the available methods, according to the severity (e.g. infoT, debugT, warningT, errorT, fatalT)
category.infoT(location, method, "User {0} logged in", new Object[] { user });

1.4  Tracing

Proper tracing should be implemented as follows:
1. Create a reference to a location object for tracing like this
private static final Location location = Location.getLocation(<class name>.class);

String method = "<method name>";
Create a String variable which contains the method name in order to enable filtering for the logs of a given method in log viewer. This must be the first statement in every method, and second in constructors. Don’t set multiple times the method name in a given method. If there are multiple methods with the same name distinguish them using parameter list like this:
String method = "<method name> (String)";

String method = "<method name> (String, String)";
2. Output the trace message using the one of the available methods, according to the severity (e.g. infoT, debugT, pathT, warningT, errorT)
location.infoT(method, "User {0} started a new transaction", new Object[] { user });
Because of performance reasons for printing parameters an object array should be used. Elements of the array can be inserted to the text with the following way:
"This is an example text. Parameter 1 is {0}, the next parameter is {1}, the last parameter is {1}, the last parameter is {2}", new Object[] { variable1, variable2, variable3 }
3. Trace the entry and the exit of the method in nontrivial cases. For methods throwing exceptions place the exit in the finally{..} clause.
location.entering(method);
try {
    …
} finally{
    … location.exiting(method);
}
Entering exiting pair should be used for all methods which have main business role and for important utility methods as well. Methods with limited functionality, or methods with only 1 or 2 log entries can be skipped.


4. Exceptions should be traced using the traceThrowableT method of the Location class.
try {
    …
    <BUSINESS OPERATIONS>
    …
} catch (Exception e) {
    …
    location.traceThrowableT(Severity.ERROR, method, "Error occurred while calling business operation with parameter {0}", new Object[] { variable}, e);
    …
}

Usually in productive systems only Error level is enabled. That’s why the log message should contain the most important values which were processed when the error occurred.
If the current class cannot handle the exception and it thrown it forward; it should be logged in all level. If we filter for a given class in the Log Viewer application which is part of a call chain we won’t be able to see the exception if we don’t log it on every level.
If the given exception is related to configuration or availability of a system component/another system in the landscape instead of location it should be reported to category, because this problem can be solved by a system administrator.


5. Input Parameter Checks

If the given method has input parameter check we can report result in a warning message.
if ( inputParameter_OrderID > 5) {
    location.warningT(method, "Wrong input parameter Order Id: {0}", new Object[] {inputParameter_OrderId});

    return;

}

6. UI Navigation

If you navigate in the Web Dynpro UI layer between pages you can log it with path severity:
location.pathT(method, "Navigate to Clientselection component");
wdThis.wdFireEventNavigationEvent(EventSources.ES_CLIENTSELECTION, eventId);
   
It other layer/application/system is called from the code it should be logged on Path level (Webservice calls, JCO calls, etc.). This way the connections between layers and components can be investigated. If the called component is in the same software Component Info level should be used.
7. Business Code
The developers should decide what are the important steps in the code which are could be interesting mainly for developers and support engineers. Some cases the messages could be important for administrators as well.
location.infoT(method, "Get identTypeDropDownKey from context");

 

ISimpleTypeModifiable myType = wdThis.wdGetAPI().getContext().getModifiableTypeOf("identTypeDropDownKey");

 

IModifiableSimpleValueSet values = myType.getSVServices().getModifiableSimpleValueSet();
 
infoT for the important values/process steps for developers.  
location.debugT(method, „Put value {0} into the list with key {1} ", new Object[] {e.getAttributeValue("value"), e.getAttributeValue("key")});

values.put((String)e.getAttributeValue("key"), (String)e.getAttributeValue("value"));
debugT for the detailed information which can help find the exact reason of a problem. If you would like to print exact value of a variable please use location.debugT. There is one exception: calling other layers. Please follow the security restrictions as well! For example don't insert highly confidential information into log and trace messages, for example passwords, PIN codes, etc. In ambiguous cases please contact the local security expert!

3.    Severity settings

Default severity settings should be:
-          INFO for categories (INFO and higher severity messages will be logged)
-          ERROR for locations (trace everything with severity ERROR or above)
Because of performance reasons ERROR level should be configured for categories as well in productive systems.

4.    Performance Considerations

If you have parameters, never build Strings yourself, always use the {n}-replacement:
location.infoT(method, "User {0} started transaction", new Object[] { user });

If you have to make calculations for traces, check first if the tracing is active

if(location.beLogged(<severity>/* e.g. WARNING */) ) {  // perform calculations and do logging/tracing
}
In this case this check should be used (of course errorT should be used instead of traceThrowableT):
locationCheckIsNeeded.png

5.    Common mistakes regarding Logging and Tracing

Using Error level instead of Info:
throwable.jpg
Printing the content of a given variable is not an error message. Debug level trace should be used
Using Debug level instead of Error:
debugLevel.jpg
Exceptions must be logged with ERROR log level. If an exception is part of normal business behavior (for example it indicates that a function call has no result) it shouldn’t log as an exception.
Incorrect log messages:
Biplab.jpg
How this log message helps to understand what happened in the code? A good log message should be understandable for everybody even without source code.
The log message should contain as many information about the working of the program as possible. In this case Error Code and Error Description could be useful. The timestamp is by default part of the log message.
traceThrowable.png
Unhandled exceptions:
unhandledException.jpg
Unhandled exceptions will be written to System.err stream and is spams the log file. Always use traceThrowableT method with Error severity
Using wrong method to print exception details in the log:
printStackTrace.png
Using print stack trace is forbidden, because it prints the stack trace into the log in barely readable format.
catching.jpg
The method “catching” will report the exception to Warning level, furthermore entering of own error message is not possible.
In both cases use location.traceThrowableT method instead.
Using traceThrowableT method instead of errorT
traceThrowable.png
If there is no exception error should be reported with errorT method.
Calling exiting method in the middle of the method
exiting.png
Entering-exiting pair should be called only once at the very beginning and end of the method. During issue investigation the investigator will think that the method has ended.
Logging closely related data into several log entry
multiple log entries.jpg
If there are multiple variable to log which are related they should be logged into one log entry. We will see the related data in one place and the logging will be faster because the reduced number of I/O operations.
location.infoT(method, „System Number Reset \n Sales Order ID: {0}\n EOSE Plan: {1}\n Quote: {2}\n Quote Line Item: {3}\n Sales Order Item: {4}\n SEN: {5}\n SSI: {6}", new Object[] {so.getID(), so.getPlan(), quoute.getName(), quote.getLineItem(), soItem.getId(), so.getSEN(), so.getSSI()});
Please note this is just an example code. Every value will be written into a new line so tit remains readable.
Method name abuse
methodNameAbuse.jpg
The method name should contain ONLY the method name, not the class name. In the Log Viewer application the location field will contain the class name and the method name. If the method name contains the class name as well this field won’t be readable anymore because it will be too long and contains the same data twice.
If there are more methods with the same name the parameter list can be added in order to distinguish between the methods.
Sensitive information in the log:
Printing sensitive personal data, like account balance or password is forbidden
Null pointer check is missing:
If a printed value is provided by a method of a class null pointer check for the class is mandatory.
Swallowed exception
try {
    …
} catch {
    //TODO write exception handling
}
In this case the exception won’t be visible in the log.

7.    Appendix

Official guide for Logging and Tracing

The reason behind this blog can be found here: [1].

 

This blog is not about writing a WDJ application for uploading a file, as there is more than enough information on SCN available explaining how to do this. It is about how to configure ICM to allow large file uploads (>100 MB) and what this implies. The configuration is valid for NetWeaver Java >= 7.10 as these releases use ICM.

 

A limitation of the maximum file size accepted by ICM is given by standard configuration. By default, ICM only accepts files not larger than 100 MB. As always, the documentation is not explaining why this is, it is just given by almighty SAP [2]:

“The default setting for the size of HTTP requests is a maximum of 100 megabytes”

 

The parameter: icm/<PROT>/max_request_size_KB


The see the current configuration of this parameter, you can use SAP MMC (you secured port 50013 to ensure not everyone can see anything of your portal, including the logs, right?).

 

largefiles1.jpg

largefiles2.jpg

This parameter needs to be changed in the ICM configuration file located on the server. As it is a default parameter, you`ll have to create the parameter first to overwrite the default value. After a restart of ICM, the change is effective and you can upload GB large files. Now, this parameter configured ICM and therefore generally valid. In case your portal is accessible by external users, you should use a reverse proxy to make sure people from outside try to crash your server by sending automatically large files to crash your server.

 

It would be nice to not find out the possible parameters using SAP Help when editing a configuration file. This is just counterproductive. Why not put all the parameters there, including a description? That’s how most (ex: open source software) configuration files are done. The file serves as documentation.

 

Example application to try it out

 

The limitation is valid for every HTTP request send to AS Java, so if you want you can try it out with a portal application, servlet, KM file upload or a WDJ application. The example here is a WDJ application. You can write your own application using the examples provided by SAP here on SCN. In case you do not want to search, here is a short description on how to code the application.

 

1. Create a view named FileUpload

largefiles3.jpg

largefiles4.jpg

2. Node element uploadFile

largefiles5.jpg

 

3. Uploading the file is triggered by the action UploadFile

 

public void onActionUploadFile(com.sap.tc.webdynpro.progmodel.api.IWDCustomEvent wdEvent )
{
  IPrivateFileUploadView.IContextElement element = wdContext.currentContextElement();
  IWDResource resource = element.getUploadFile();
  if (element.getUploadFile() != null) {
  // do something
} else {}

 

 

This is all it needs to have a WDJ application that allows to upload a file. Note that the node element handling the file is of type Resource. During the upload, WDJ creates a temporary file on the server that will be stored there until the resource is not any longer used.

 

 

Appendix

 

[1] http://scn.sap.com/thread/3278398

[2] SAP Help

 

Some information from SAP Help and SCN about WDJ and file upload / download

 

Title

Link

Release

Uploading and Downloading Files in Web Dynpro

http://help.sap.com/saphelp_nw04/helpdata/en/43/85b27dc9af2679e10000000a1553f7/content.htm

7.0

Web Dynpro Java Demo Toolkit

http://wiki.sdn.sap.com/wiki/display/WDJava/Web+Dynpro+for+Java+Demo+Kit

>= 7.0

How to Upload and Download Files in an Web Dynpro for Java Application

http://scn.sap.com/docs/DOC-2601

>= 7.11

SAP Portal KM: Create a resouce

http://help.sap.com/saphelp_nw2004s/helpdata/en/43/68a36ba327619ae10000000a1553f6/frameset.htm

7.0

Loading the InputStream at FileDownload on Demand

http://help.sap.com/saphelp_nw73/helpdata/de/42/f6ec5a09321bc7e10000000a11466f/frameset.htm

7.3

Uploading and Downloading Files in Web Dynpro Java

http://scn.sap.com/docs/DOC-2561

7.0

Java Connection Pool - Design and Sample Implementation

                                                                                                    -  Aavishkar Bharara

Introduction

 

In the world of application designing, we often encounter the need to fetch, update or delete objects stored in the database. To enable this, we would require a database connection object. Opening and closing the database connection object can be very costly as it involves, creating the network connection to the database followed by doing the authentication using the username and password. Hence, database connection is a scarce resource and need to be utilized efficiently and effectively.

 

 

Connection Pooling

The connection pooling helps to utilize and efficiently re-use the database connection in a multithreading environment. The idea is to share the database connections (limited connections) with the users (unlimited numbers) in the most efficient way.

 

 

Design

Constraints

There are multiple design options for creating an efficient and re-usable connection pool application programming interface.

The design constraints include:-

  • A singleton "Connection Pool" class
  • An option to "request" for the connection from the connection pool.
  • An option to "release" the connection to the connection pool.

Apart from these core design constraints, there are some administration requirements as well, which includes:-

  • Tracking of "who" and "when" was the connection requested. This helps to track "connection leaks" in the code and take corrective steps accordingly.
  • Tracking of "lost" connections or "idle" connections which were not returned back to the connection pool and "claim" them back to the connection pool.

Design Options

These requirements are very similar to many frameworks already available in the market.Microsoft COM/DCOM provides a common interface called as IKNOWN interface to accomplish the similar set of requirements.  ...see link for more details

 

Java has the similar requirements for the "Garbage Collector" usage to free up unused java objects....see link for more details


Implementation

Interface IConnectionPool

An interface IConnectionPool provides the basic methods of the connection pool class.


IConnectionPool.jpg

Interface : IConnectionPoolAdmin

 

This provides the administrative functions for the connection pool class.



Connection Container Class

The implementation starts with creating a container for the "connection object" first.


 

This connection container class stores the attributes of the connection like,

  • Connection is in use or idle
  • Who and when accessed the connection


Connection Class

The connection class implements the IConnectionPool interface to provide the basic features of connection pool. It also implements the IConnectionPoolAdmin to provide the administrative functions to the connection pool implementation.

 

        public class ConnectionPool implements IConnectionPool, IConnectionPoolAdmin {

 

This implements the "singleton" feature in the Connnection via the "getInstance()" function. The function getConnection() has the complete logic built to get "idle connection" and set the required attributes to the connection object so that it is "marked" as currently "in-use".

A Sample Implemented Code

 

Attached is the complete implemented code using the jar file AviConPool.jar which you can include in your project and start re-using it. The following set of methods needs to be called to utilize this jar file.

 

First (and only once) Call should be,

 

      ConnectionPool.getInstance().setup(
                    String DBDriver, String DBURL, String username, String password, int maxConnections);

This needs to be called from the application central component for once.


Subsequently, the calls to the connection pool would be something like this,

 

public void functionABC

{


  Connection con = ConnectionPool.getInstance().getConnection();


  try {

  <<< Write Your Code Here >>>

                       

                        Statement stmt = con.createStatement();

 

  ResultSet rs = stmt

  .executeQuery("select ......");


  if (rs.next()) {

  // Read Data

  }

 

  // Help GC

  rs.close();

  stmt.close();

  rs = null;

  stmt = null;

  } catch (Exception ex) {

  // Log Errors

  }

  finally

  {

  ConnectionPool.getInstance().releaseConnection(con);

  }

}

 



Also, attaching the jar file [ConPool.txt which can be renamed to ConPool.jar] which you can use in building your applications.

In software development, a general rule is to not reinvent the wheel. This meas: If there is already a library available, that does the job, use that library. Without this thinking, even the simplest projects would consume too much time in solving already solved problems. A drawback is that the project will depend on a lot of external libraries. Managing these library dependencies is important. Java projects are no exception to that. For instance, there are many standard classes available from Apache that everyone uses, but are not part of core Java.

 

In the Java world, two main tools help to take care of this: Maven and Ivy. Both allow to define the dependencies for each project and download them from a central repository. There are public accessible Maven repositories available and while it is easy to find there the needed library, it raises a simple question: is this the right way for your project to do?

 

Once published there, the library can be downloaded from anyone. Most probably, this is not what you want for your internal project. When you have several developers, makes it sense that each one is accessing the outside repository and downloads hundreds of MB when resolving the library devependencies? When a developer creates a new internal JAR, how to distribute it to the other developers? For code quality analysis, how to get the jars needed for a binary analysis?


There are solutions available for internal repository managers that also act as a proxy for public repositories. The most famous ones are:

  • Nexus
  • Artifactory

 

Installation, configuration and usage of both is simple. In case the Java project is already using maven or ivy to resolve dependencies, it is only changing the name of the repository server to the internal one. In case a project depends on an external library, the repository manager connects the public maven server and downloads them. As all developers are using the internal server, caches help to minimize traffic and reduce overall time needed to resolve a dependency. Libraries created by the Java projects can be uploaded to the internal repository manager and are automatically accessible by other developers. Maven and Ivy take care of this.

 

What about SAP Java jars? How to get these and publish to Nexus / Artifactory? SAP isn’t making them available in a public repository manager. There is also no other way given by SAP to import the jars. What you get are the SCs and DCs. NWDI is the repository manager for SAP Java, but when you use continuous integration, NWDI is not the best option.

 

Using NWDS and NWDI it is possible to sync all DCs to a local computer. This sync process creates a folder structure and copies the associated files, including the jar files. DCs have a public part concept, and each defined public part resolves to a jar file.

 

To get the jars, open the Development Infrastructure perspective in NWDS and select the DC to get the jar from and sync it.

rm1.jpg

Where are the files stored? In the workspace directory for NWDI projects. In my case, I have a workspace named test and only 1 NWDI connection (resolved to 0): <path>\test.jdi\0\DCs\sap.com

 

If you do this for all of SAP`s DCs, the directory will look like

rm2.jpg

The jar files are stored depending on their DC name.

rm3.jpg

Tc/je/usermanagement/api resolves to: <path>\test.jdi\0\DCs\sap.com\tc\je\usermanagement\api\. The jar file is stored in the standard directory, that is: _comp\gen\default\public\api\lib\java

rm4.jpg

This is the jar that needs to be uploaded to your repository manager server. This can be done manually or automated by a script. I developed a small script for that that also replaces the ~ with a . and that I can run from Jenkins.

rm5.jpg

In Artifactory, the artifact is shown with additional meta data, like age, size and download statistics.

rm6.jpg

Searching for a class is possible as well as browsing the content of the jar to see the individual class files.

rm7.jpg



Today my colleague tells me with one tip regarding Eclipse auto completion setting, which I think is useful in my daily life.

 

 

There are lots of standard classes and those class/method created on my own, however when I type some characters there is no auto completion dropdown list. For example I have already created one method named "consumeABAPFM", however even after I type "consum", there is still not any auto completion function provided.

clipboard1.png

However, by choosing "Windows->Preference->Java->Editor->Content Assistant", we can achieve what we expect.

 

 

Maintain "Auto activation triggers for JAVA" with value ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".

clipboard2.png


After that whenever we type some characters the auto completion dropdown list will be there and refreshed with 200 ms.

clipboard3.png


In our JAVA development if there is no source code attached for a class like below,

clipboard1.png

We have no chance to view its source code.

clipboard2.png

However you can use an open source tool, JAD, to decompile the class file so that you can view its source code.


There is also an available plugin for eclipse which you can download from this link.


1. Download the proper Jad Eclipse plugin according to the version of your Eclipse:

clipboard6.png

2. Download the proper JAD.exe file according to your OS type:

clipboard7.png

clipboard8.png

3. put the JAD plugin to your Eclipse plugin folder:

clipboard9.png

4. Put the JAD.exe to the bin folder of your JRE installation:

clipboard10.png

5. Restart your Eclipse, in menu Windows->Preference->Java, you can find there is a new option JadClipse, maintain the path of your JAD.exe to "Path to decompiler":

clipboard11.png

6. Now click F3 on the class which you would like to view its source code, the JAD will decompile it for you:

clipboard12.png



  These documents will help you to creating the web service for updating the data in java table using EJB and JPA.

1.     Create Dictionary DC from Dictionary Perspective.

1.png

2.     Create Table in dictionary.

2.png

3.     Build and deploy the Dictionary DC.

4.     Create EJB  application-Select DC type as EJB Module.

4.png

5.     Give EJB application name and click next.

5.png

6.     Select JPA and finish.

6.png

7.     Create Enterprise application for EJB application .

7.png

8.     Give name of enterprise application.

8.png

9.     Add  ejb application(test_app) to ear project and finish.

9.png

10.     Select Sap data source aliases provider module of the ear DC from project facets and apply.

10.png

11.     Check alias name of data base in the ear app.

11.png

12.     Open data-source-aliases.xml from META-INF folder and note the alias name.

12.png

13.     Create  connection from EJB application to dictionary.

13.png

14.     Define Alias name in persistence.xml.

14.png

15.     Generate Entities from Table in EJB.

15.png

16.     After creating the entity class, code as below.

16.png

17.     Create class for dto in EJB.

17.png

18.     Generate the getter and setter for this variable.

18.png

19.     Create an interface in the EJB DC and declare save method as below.

19.png

20.     Create a session bean in EJB DC.

20.png

21.     Include Entity Manager and Persistence Context in session bean.

21.png

22.     Define save operation in session bean.

22.png

23.     Create a web service for session bean.

23.png

24.     Build and deploy the EAR and EJB project.

25.     Test the web service from WSNavigator.

Steve Carlton

ABAP Enigma Machine

Posted by Steve Carlton Oct 8, 2013

I got interested in the story of the Enigma machines and Bletchly Park and ended up writing this, just for kicks

Now, is someone can write something in ABAP to decypher Enigma messages, please let me know.

 

*&---------------------------------------------------------------------*
*  Report  Z_ENIGMA
*
*&---------------------------------------------------------------------*
* This is a3 wheel Enigma coding machine emulator using genuine wheel settings.
* Sequence is this :-
* Input -> Plugboard -> Wheel 3,2,1 -> Reflector -> Wheel 1,2,3 -> Plugboard -> Output
*
* The plugboard is a set of connections between the wheel input and either
* the keyboard (i/P) or lightboard (o/p)
*
* An enigma wheel has a set of 26 i/p connections and 26 o/p connections
* It has a ring around the outside so these connections can be
* shifted relative to the characters that are displayed on its outer edge.
* It has a turnover point where the next wheel in the set moves 1 position
* (think how a car odometer works when it moves from 9 to 10)
*
* When in use, the initial settings have to be known by the receiving machine
* So the initial settings are encoded using some other method and transmitted
* to the receiver first. This is called public key encryption, because the key
* is known publicly (but not how to use it).
*&---------------------------------------------------------------------*
* Text symbols are defined as :
* 001 = "Enigma Settings: Wheel order, start positions and plugboard"
* 002 = "Text to be encrypted / decrypted. Characters A to Z only. No puncuation / numbers."
*
* Selection Texts are defined as :
* INIT1    = "Initial Position Wheel 1"
* INIT2    = "Initial Position Wheel 2"
* INIT3    = "Initial Position Wheel 3"
* P_PLUGB  = "Use plugboard mapping?"
* W1      = "Wheel 1"
* W2      = "Wheel 2"
* W3      = "Wheel 3"
*&---------------------------------------------------------------------*

REPORT  z_enigma .

CONSTANTS:
           c_ref_b    TYPE char26 VALUE 'YRUHQSLDPXNGOKMIEBFZCWVJAT',
           c_plugb    TYPE char26 VALUE 'ZBCDEFHGIJKLNMOPQRSTUVWXYA',
           c_alpha    TYPE char26 VALUE 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
           c_wheel1   TYPE char26 VALUE 'EKMFLGDQVZNTOWYHXUSPAIBRCJ',
           c_wheel2   TYPE char26 VALUE 'AJDKSIRUXBLHWTMCQGZNPYFVOE',
           c_wheel3   TYPE char26 VALUE 'BDFHJLCPRTXVZNYEIWGAKMUSQO',
           c_wheel4   TYPE char26 VALUE 'ESOVPZJAYQUIRHXLNFTGKDCMWB',
           c_wheel5   TYPE char26 VALUE 'VZBRGITYUPSDNHLXAWMJQOFECK',
           c_wheel6   TYPE char26 VALUE 'JPGVOUMFYQBENHZRDKASXLICTW',
           c_wheel7   TYPE char26 VALUE 'NZJHGRCXMYSWBOUFAIVLPEKQDT',
           c_wheel8   TYPE char26 VALUE 'FKQHTLXOCBJSPDZRAMEWNIUYGV',
           c_turn1    TYPE char1  VALUE 'R',
           c_turn2    TYPE char1  VALUE 'F',
           c_turn3    TYPE char1  VALUE 'W',
           c_turn4    TYPE char1  VALUE 'K',
           c_turn5    TYPE char1  VALUE 'A',
           c_turn6    TYPE char2  VALUE 'AN', "Getting sneaky by using 2 turnover positions
           c_turn7    TYPE char2  VALUE 'AN',
           c_turn8    TYPE char2  VALUE 'AN'.

DATA: v_output TYPE string,
      v_source TYPE string,
      v_temp   TYPE string,
      v_length TYPE i,
      source   TYPE string,
      wheel3   TYPE char26,
      wheel2   TYPE char26,
      wheel1   TYPE char26,
      alpha    TYPE char26,
      turn1    TYPE char1,
      turn2    TYPE char1,
      fname    TYPE char8.

TYPES : BEGIN OF ty_wrd ,
         text TYPE char80 ,
       END OF ty_wrd .

DATA: char_in    TYPE char1,
      c1    TYPE char1,
      idx   TYPE i,
      pos   TYPE i,
      char_out TYPE char1,
      lt_text  TYPE TABLE OF ty_wrd WITH HEADER LINE,
      gc_docking        TYPE REF TO cl_gui_docking_container,
      gc_text_editor    TYPE REF TO cl_gui_textedit,
      gt_text           TYPE TABLE OF tline-tdline,
      gs_text           TYPE tline-tdline.

FIELD-SYMBOLS <data> TYPE any.

*======================================================================*
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE text-001.
* Choose 3 wheels
PARAMETERS: w1 TYPE ddtabind DEFAULT 1,
            w2 TYPE ddtabind DEFAULT 2,
            w3 TYPE ddtabind DEFAULT 3.
SELECTION-SCREEN SKIP 1.

* Set wheel start position
PARAMETERS: init1 TYPE sf_char1 DEFAULT 'M',
            init2 TYPE sf_char1 DEFAULT 'C',
            init3 TYPE sf_char1 DEFAULT 'K'.

SELECTION-SCREEN SKIP 1.
PARAMETERS: p_plugb AS CHECKBOX DEFAULT 'X'.

SELECTION-SCREEN END OF BLOCK b1.

SELECTION-SCREEN SKIP 2.
SELECTION-SCREEN COMMENT /1(82) text-002.


*======================================================================*
INITIALIZATION.
  PERFORM initialization.

*======================================================================*
AT SELECTION-SCREEN OUTPUT.
  TRANSLATE init1 TO UPPER CASE.
  TRANSLATE init2 TO UPPER CASE.
  TRANSLATE init3 TO UPPER CASE.

AT SELECTION-SCREEN ON BLOCK b1.
  IF w1 IS INITIAL OR
     w2 IS INITIAL OR
     w3 IS INITIAL OR
     w1 > 8 OR w2 > 8 OR w3 > 8.
    MESSAGE s001(00) WITH 'Select 3 wheel numbers from 1 to 8'.
  ENDIF.

  IF w3 = w2 OR
     w3 = w1 OR
     w2 = w1.
    MESSAGE s001(00) WITH 'Wheels cannot be used in more that 1 position'.
  ENDIF.

  IF init1 IS INITIAL OR
     init1 NA sy-abcde.
    MESSAGE s001(00) WITH 'Set start position wheel 1 A to Z'.
  ENDIF.
  IF init2 IS INITIAL OR
     init2 NA sy-abcde.
    MESSAGE s001(00) WITH 'Set start position wheel 2 A to Z'.
  ENDIF.
  IF init3 IS INITIAL OR
     init3 NA sy-abcde.
    MESSAGE s001(00) WITH 'Set start position wheel 3 A to Z'.
  ENDIF.

*======================================================================*
START-OF-SELECTION.

* Load selected wheels into work areas
  CONCATENATE 'C_WHEEL' w1 INTO fname.
  ASSIGN  (fname) TO <data>.
  wheel1 = <data>.

  CONCATENATE 'C_WHEEL' w2 INTO fname.
  ASSIGN  (fname) TO <data>.
  wheel2 = <data>.
  CONCATENATE 'C_TURN' w2 INTO fname.
  ASSIGN  (fname) TO <data>.
  turn1 = <data>.


  CONCATENATE 'C_WHEEL' w3 INTO fname.
  ASSIGN  (fname) TO <data>.
  wheel3 = <data>.
  CONCATENATE 'C_TURN' w3 INTO fname.
  ASSIGN  (fname) TO <data>.
  turn2 = <data>.

  PERFORM enigma.

*======================================================================*
FORM initialization.

* Add free text box to selection screen
  CREATE OBJECT gc_docking
    EXPORTING
      repid     = sy-repid
      dynnr     = sy-dynnr
      side      = gc_docking->dock_at_bottom
      extension = 200.

  CREATE OBJECT gc_text_editor
    EXPORTING
      parent = gc_docking.

  IMPORT gt_text FROM MEMORY ID sy-repid.
* Put in sample text
  IF gt_text IS INITIAL.
    gs_text =  'ZKA NTCKV KOGZE WAO UPHZX HZSY YNI FKMB AFP'.
* Using wheels 123 at MCK with plugboard turned on
* this will decypher to "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"
    APPEND gs_text TO gt_text.
  ENDIF.

  CALL METHOD gc_text_editor->set_text_as_r3table
    EXPORTING
      table  = gt_text
    EXCEPTIONS
      OTHERS = 1.

ENDFORM.                    " INITIALIZATION
*&---------------------------------------------------------------------*
*&      Form  enigma
*&---------------------------------------------------------------------*
FORM enigma.

* Get text from selection screen screen editor
  REFRESH gt_text.
  CALL METHOD gc_text_editor->get_text_as_r3table
    IMPORTING
      table  = gt_text
    EXCEPTIONS
      OTHERS = 1.

  CALL FUNCTION 'CONVERT_TABLE_TO_STRING'
    EXPORTING
      i_tabline_length = 132
    IMPORTING
      e_string         = source
    TABLES
      it_table         = gt_text.

* Shift to upper case and remove all spaces
  TRANSLATE source TO UPPER CASE.
  v_source = source.
  CONDENSE source NO-GAPS.
  v_length = strlen( source ).

* Order is this :-
* Input -> Plugboard -> Wheel 3,2,1 -> Reflector -> Wheel 1,2,3 -> Plugboard -> Output
  CLEAR : v_temp, v_output.

  DO v_length TIMES.

* get input character from keyboard
    pos = sy-index - 1.
    char_in = source+pos(1).

* Validate only alphabetic characters used
    IF char_in NA sy-abcde.
      MESSAGE s001(00) WITH 'Only letters A to Z allowed'.
      LEAVE LIST-PROCESSING.
    ENDIF.

* Plugboard mapping inbound
    PERFORM plugboard USING 'I' CHANGING char_in.

* Wheel 3
* Wheel 3 always steps on 1 position before encoding starts
    CLEAR pos.
    SEARCH c_alpha FOR init3.
    pos = sy-fdpos + 1. "11
    PERFORM index_wrap CHANGING pos.
    init3 = c_alpha+pos(1).

* Setup wheel
    CONCATENATE 'C_WHEEL' w3 INTO fname.
    ASSIGN  (fname) TO <data>.
    PERFORM set_wheel USING  <data>
                             init3
                    CHANGING wheel3.

* Look for index of entered character
    SEARCH c_alpha FOR char_in.

* Get wheel character.
    c1 = wheel3+sy-fdpos(1).

* Look for index of o/p character
    PERFORM set_wheel USING  c_alpha
                         init3
                CHANGING alpha.
    SEARCH alpha FOR c1.
    idx = sy-fdpos.

* Wheel 2 turnover
    IF turn1 CA init3.
      CLEAR pos.
      SEARCH c_alpha FOR init2.
      pos = sy-fdpos + 1.
      PERFORM index_wrap CHANGING pos.
      init2 = c_alpha+pos(1).
    ENDIF.

* Wheel 2
* Setup wheel
    CONCATENATE 'C_WHEEL' w2 INTO fname.
    ASSIGN  (fname) TO <data>.
    PERFORM set_wheel USING  <data>
                             init2
                    CHANGING wheel2.

    c1 = wheel2+idx(1).

* Look for index of o/p character
    PERFORM set_wheel USING  c_alpha
                         init2
                CHANGING wheel2.
    SEARCH wheel2 FOR c1.
    idx = sy-fdpos.


* Wheel 1 turnover
    IF turn2 CA init2.
      CLEAR pos.
      SEARCH c_alpha FOR init1.
      pos = sy-fdpos + 1.
      PERFORM index_wrap CHANGING pos.
      init1 = c_alpha+pos(1).
    ENDIF.

* Wheel 1
* Setup wheel
    CONCATENATE 'C_WHEEL' w1 INTO fname.
    ASSIGN  (fname) TO <data>.
    PERFORM set_wheel USING  <data>
                             init1
                    CHANGING wheel1.

    c1 = wheel1+idx(1).

* Look for index of o/p character
    PERFORM set_wheel USING  c_alpha
                         init1
                CHANGING wheel1.
    SEARCH wheel1 FOR c1.
    idx = sy-fdpos.
    c1 = c_alpha+idx(1).

* Reflector
    SEARCH c_ref_b FOR c1.
    c1 = c_alpha+sy-fdpos(1).
    idx = sy-fdpos.

* wheel 1 again
    PERFORM set_wheel USING  c_alpha
                      init1
             CHANGING wheel1.
    c1 = wheel1+idx(1).

    CONCATENATE 'C_WHEEL' w1 INTO fname.
    ASSIGN  (fname) TO <data>.
    PERFORM set_wheel USING  <data>
                             init1
                    CHANGING wheel1.
    SEARCH wheel1 FOR c1.
    idx = sy-fdpos.

* wheel 2 again
    PERFORM set_wheel USING  c_alpha
                      init2
             CHANGING wheel2.
    c1 = wheel2+idx(1).

    CONCATENATE 'C_WHEEL' w2 INTO fname.
    ASSIGN  (fname) TO <data>.
    PERFORM set_wheel USING  <data>
                             init2
                    CHANGING wheel2.
    SEARCH wheel2 FOR c1.
    idx = sy-fdpos.

* wheel 3 again
    PERFORM set_wheel USING  c_alpha
                      init3
             CHANGING wheel3.
    c1 = wheel3+idx(1).

    CONCATENATE 'C_WHEEL' w3 INTO fname.
    ASSIGN  (fname) TO <data>.
    PERFORM set_wheel USING  <data>
                             init3
                    CHANGING wheel3.
    SEARCH wheel3 FOR c1.
    idx = sy-fdpos.

    char_out = c_alpha+idx(1).

* Plugboard mapping outbound
    PERFORM plugboard USING 'O' CHANGING char_out.

* Build output string
    CONCATENATE v_temp char_out INTO v_temp.
  ENDDO.

* Put spaces back in same place as in selection screen text
  SHIFT v_source LEFT DELETING LEADING space.
  v_length = strlen( v_source ).
  CLEAR: pos, idx.

  DO v_length TIMES.
* get selection screen character
    c1 = v_source+pos(1).

    IF c1 IS INITIAL.
      CONCATENATE v_output ' ' INTO v_output RESPECTING BLANKS.
      idx = idx + 1.
      pos = pos + 1.
      CONTINUE.
    ELSE.
      sy-fdpos = pos - idx.
      c1 = v_temp+sy-fdpos(1).
      CONCATENATE v_output c1 INTO v_output.
      pos = pos + 1.
    ENDIF.

  ENDDO.


* Write source to result screen
  REFRESH lt_text.
  CALL FUNCTION 'CONVERT_STRING_TO_TABLE'
    EXPORTING
      i_string         = v_source
      i_tabline_length = 80
    TABLES
      et_table         = lt_text.

  FORMAT INTENSIFIED ON.
  WRITE / 'Input text.'.
  FORMAT INTENSIFIED OFF.
  LOOP AT lt_text.
    SHIFT lt_text-text LEFT DELETING LEADING space.
    WRITE / lt_text-text.
  ENDLOOP.

* Write result to result screen
  REFRESH lt_text.
  CALL FUNCTION 'CONVERT_STRING_TO_TABLE'
    EXPORTING
      i_string         = v_output
      i_tabline_length = 80
    TABLES
      et_table         = lt_text.
  WRITE /.
  FORMAT INTENSIFIED ON.
  WRITE / 'Output text.'.
  FORMAT INTENSIFIED OFF.
  LOOP AT lt_text.
    SHIFT lt_text-text LEFT DELETING LEADING space.
    WRITE / lt_text-text.
  ENDLOOP.

  WRITE /.
  FORMAT INTENSIFIED ON.
  WRITE / 'If you copy the output text and use that as the input text'.
  WRITE / 'with the same initial setup on the selection screen.'.
  WRITE / 'your output will be deciphered or enciphered as required'.

ENDFORM.                    "enigma
*&---------------------------------------------------------------------*
*&      Form  plugboard
*&---------------------------------------------------------------------*
FORM plugboard USING p_direction TYPE char1
               CHANGING p_char TYPE char1.
* This is a simple x to y type mapping
*  Not all charcaters have to be mapped
* EG. You could leave everything as it comes out of the wheels
* except, say, mapping A to G (and therefore G to A)
*
* The default in this pgm only map A/Z and G/H in the plugboard
* c_alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
* c_plugb = 'ZBCDEFHGIJKLNMOPQRSTUVWXYA',
*            ^     ^^                 ^

  CHECK p_plugb = 'X'. " is plugboard mapping ON

  IF p_direction = 'I'.
* Input from keyboard to wheels
    SEARCH c_alpha FOR p_char.
    p_char = c_plugb+sy-fdpos(1).
  ELSE.
* Output from wheels to lightboard
    SEARCH c_plugb FOR p_char.
    p_char = c_alpha+sy-fdpos(1).
  ENDIF.

ENDFORM.                    "plugboard
*&---------------------------------------------------------------------*
*&      Form  index_wrap
*&---------------------------------------------------------------------*
* This is used to correct the wheel index in case of wrap-around
* required to make the linear wheel string act like a circular wheel
FORM index_wrap CHANGING p_i TYPE i.
  IF p_i > 25.
    p_i = p_i - 25.
  ELSEIF p_i < 0.
    p_i = p_i + 25.
  ENDIF.
ENDFORM.                    "index_wrap
*&---------------------------------------------------------------------*
*&      Form  set_wheel
*&---------------------------------------------------------------------*
* Set wheel characters so start position charcater is at position 1.
* EG :
* Wheel 1 with start position is, say, W
* EKMFLGDQVZNTOWYHXUSPAIBRCJ
* -------------^------------
* becomes:
* WYHXUSPAIBRCJEKMFLGDQVZNTO

FORM set_wheel USING     p_wheel_in  TYPE char26
                         p_start     TYPE char1
                CHANGING p_wheel_out TYPE char26.


  DATA: l_index TYPE i,
        l_char  TYPE char1.

  CLEAR p_wheel_out.

  SEARCH c_alpha FOR p_start.
  l_index = sy-fdpos.

  DO 26 TIMES.
    IF l_index > 25.
      l_index = 0.
    ENDIF.

    l_char = p_wheel_in+l_index(1).
    CONCATENATE p_wheel_out l_char
    INTO p_wheel_out.

    l_index = l_index + 1.
  ENDDO.


ENDFORM.                    "set_wheel

Steve Carlton

Playing with JCo3

Posted by Steve Carlton Oct 8, 2013

So I downloaded the Java connector sapjco3.jar and the examples and started to play.

I soon got put off by the fact that all the examples require a username and password to be stored in a file and the idoc example I found was a bit over complicated for someone coming from an ABAP background.

 

Much Googling later and here is what I ended up with. This stores the system connection details in a file but NOT the username and password.

The idoc is created by calling a RFC function and the bit that casts a JCoStructure into a String (to populate the sdata field) is quite useful (I think).

 

--------- Start Java Code --------------
import java.util.HashMap;
import java.util.Properties;
import java.io.FileInputStream;
import java.io.IOException;

import com.sap.conn.jco.*;
import com.sap.conn.jco.rt.*;
import com.sap.conn.jco.ext.*;

/**
* This is the example that SAP should have delivered It keeps the system name
* etc in a file but not the user and password
*
* It will call a RFC function to create an idoc and print out the returned data
*
* Create a file called ABAP_AS.jcoDestination as below
* jco.client.lang=<language>
* jco.client.client=<client>
* jco.client.sysnr=<system number>
* jco.client.ashost=<hostname>
*
* Enter the required values starting lines: 158,185,196

* Install sapjco3,

* set environment variables: classpath, lib_path and path

* Compile with : javac iDocTest.java

* Run with command java iDocTest
*/
public class iDocTest {

    static String ABAP_AS = "ABAP_AS";

    static class MyDestinationDataProvider implements DestinationDataProvider {

        private DestinationDataEventListener eL;
        private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();

        public Properties getDestinationProperties(String destinationName) {
            try {
                //read the destination from DB
                Properties p = secureDBStorage.get(destinationName);

                if (p != null) {
                    //check if all is correct, for example
                    if (p.isEmpty()) {
                        throw new DataProviderException(DataProviderException.Reason.INVALID_CONFIGURATION, "destination configuration is incorrect", null);
                    }

                    return p;
                }

                return null;
            } catch (RuntimeException re) {
                throw new DataProviderException(DataProviderException.Reason.INTERNAL_ERROR, re);
            }
        }

        //An implementation supporting events has to retain the eventListener instance provided
        //by the JCo runtime. This listener instance shall be used to notify the JCo runtime
        //about all changes in destination configurations.
        public void setDestinationDataEventListener(DestinationDataEventListener eventListener) {
            this.eL = eventListener;
        }

        public boolean supportsEvents() {
            return true;
        }

        //implementation that saves the properties in a very secure way
        void changeProperties(String destName, Properties properties) {
            synchronized (secureDBStorage) {
                if (properties == null) {
                    if (secureDBStorage.remove(destName) != null) {
                        eL.deleted(destName);
                    }
                } else {
                    secureDBStorage.put(destName, properties);
                    eL.updated(destName); // create or updated
                }
            }
        }
    } // end of MyDestinationDataProvider

    static Properties getDestinationPropertiesFromUI() {

        Properties userProperties;
        Properties sapProperties;
        String JCO_CLIENT;
        String JCO_LANG;
        String JCO_SYSNR;
        String JCO_HOST;

        sapProperties = new Properties();
        userProperties = new Properties();

// Load ABAP_AS.jcoDestination file
        try {
            sapProperties.load(new FileInputStream("ABAP_AS.jcoDestination"));

        } catch (IOException ex) {
            ex.printStackTrace();
        }
        // Get fixed connection details from properties file 
        JCO_LANG = sapProperties.getProperty("jco.client.lang");
        JCO_HOST = sapProperties.getProperty("jco.client.ashost");
        JCO_SYSNR = sapProperties.getProperty("jco.client.sysnr");
        JCO_CLIENT = sapProperties.getProperty("jco.client.client");
// Set connection details from strings retrieved fromn file above           
        userProperties.setProperty(DestinationDataProvider.JCO_ASHOST, JCO_HOST);
        userProperties.setProperty(DestinationDataProvider.JCO_SYSNR, JCO_SYSNR);
        userProperties.setProperty(DestinationDataProvider.JCO_CLIENT, JCO_CLIENT);
        userProperties.setProperty(DestinationDataProvider.JCO_LANG, JCO_LANG);

// Get username and password 
        System.console().printf("Enter user:");
        userProperties.setProperty(DestinationDataProvider.JCO_USER, System.console().readLine());

        System.console().printf("Enter password:");
        userProperties.setProperty(DestinationDataProvider.JCO_PASSWD, System.console().readLine());


        return userProperties;

    }

    public static void iDocCall(String destName) throws JCoException {
        //Declare destination for function call
        JCoDestination destination = JCoDestinationManager.getDestination(destName);
        String client = destination.getClient();

        // Declare function and check it exists
        JCoFunction idoc_input = destination.getRepository().getFunction("IDOC_INBOUND_SINGLE");
        if (idoc_input == null) {
            throw new RuntimeException("IDOC_INBOUND_SINGLE not found in SAP.");
        }

// Declare function fields, structures and tables
//JCoFunction is container for function values. Each function contains separate
//containers for import, export, changing and table parameters.
//To set or get the parameters use the APIS setValue() and getXXX().   

// Declare:       
// Import Fields   
//      idoc_input.getImportParameterList().setValue("MASS_PROCESSING", " ");

// Structures
        JCoStructure idoc_control = idoc_input.getImportParameterList().getStructure("PI_IDOC_CONTROL_REC_40");

//Tables       
        JCoTable idoc_data = idoc_input.getTableParameterList().getTable("PT_IDOC_DATA_RECORDS_40");

// Populate function fields, structures and tables     
// To create the idoc we only need to set the control and data
//iDoc Control
        idoc_control.setValue("MANDT", client);
        idoc_control.setValue("DIRECT", "2");
        idoc_control.setValue("MESTYP", "MBGMCR");
        idoc_control.setValue("IDOCTYP", "MBGMCR02");
        idoc_control.setValue("CIMTYP", "ZMBGMCR02001");
        idoc_control.setValue("SNDPOR", <enter_sndpor>);
        idoc_control.setValue("SNDPRT", <enter_sndprt>);
        idoc_control.setValue("SNDPRN", <enter_sndprn>);
        idoc_control.setValue("RCVPOR", <enter_rcvpor>);
        idoc_control.setValue("RCVPRT", <enter_rcvprt>);
        idoc_control.setValue("RCVPRN", <enter_rcvprn>);

//iDoc Data           
        JCoRepository repository = destination.getRepository();
        JCoStructure str_head, str_item;
        JCoRecordMetaData meta_head, meta_item;
        String sdata = "";

//Create structures for sdata population
        meta_head = repository.getStructureDefinition("E1BP2017_GM_HEAD_01");
        str_head = JCo.createStructure(meta_head);

        meta_item = repository.getStructureDefinition("E1BP2017_GM_ITEM_CREATE");
        str_item = JCo.createStructure(meta_item);

        // Write idoc data
        idoc_data.appendRows(2); // Add 4 rows to internal table
        idoc_data.setValue("MANDT", client);
        idoc_data.setValue("SEGNUM", "1");
        idoc_data.setValue("PSGNUM", "0");
        idoc_data.setValue("SEGNAM", "E1BP2017_GM_HEAD_01");

        str_head.setValue("HEADER_TXT", "Java test");
        str_head.setValue("PSTNG_DATE", "20131008");
        str_head.setValue("REF_DOC_NO", "12345");
        sdata = ConvertToSDATA(str_head, meta_head);
        idoc_data.setValue("SDATA", sdata);

        idoc_data.nextRow(); // Move to next row
        idoc_data.setValue("MANDT", client);
        idoc_data.setValue("SEGNUM", "2");
        idoc_data.setValue("PSGNUM", "0");
        idoc_data.setValue("SEGNAM", "E1BP2017_GM_ITEM_CREATE");
        str_item.setValue("MATERIAL", <enter_material>);
        str_item.setValue("BATCH", <enter_batch>);
        str_item.setValue("ENTRY_QNT", <enter_qty>);
        str_item.setValue("EXPIRYDATE", <enter_sled_date>);
        sdata = ConvertToSDATA(str_item, meta_item);
        idoc_data.setValue("SDATA", sdata);

 

//To set or get the parameters use the APIS setValue() and getValue(). 
        idoc_input.getTableParameterList().setValue("PT_IDOC_DATA_RECORDS_40", idoc_data);

// Execute the function call
        try {
            idoc_input.execute(destination);
        } catch (AbapException e) {
            System.out.println(e.toString());
            return;
        }

        // Get returned idoc number
        String idoc_number;
        idoc_number = idoc_input.getExportParameterList().getString("PE_IDOC_NUMBER");

        // Remove leading zeros from idoc number
        idoc_number = idoc_number.replaceFirst("^0*", "");
        if (idoc_number.isEmpty()) {
            idoc_number = "0";
        }
        // Write out idoc number
        System.out.println("Created idoc number " + idoc_number);

    }

    public static String ConvertToSDATA(JCoStructure data_in, JCoRecordMetaData meta_in) {
        /*
        Simply casting the structure to a string will fail (won't compile)
        Concatenating the structure fields into a string will remove all the spaces
        so the field alignment is lost.
        The solution is to create a StringBuilder object of the correct length
        Initialize it with all spaces and then, using the structure metadata,
        insert each field at the correct start position
        * NB: This works for idocs where all fields are character types
        */
        String sdata = "";
        int count = 0;
        int Start = 0;
        int End = 0;
        int Len = 0;

        // Declare string builder as 1000 long and fill with spaces
        StringBuilder strB = new StringBuilder(" ");
        strB.setLength(1000);
        ClearString1000(strB, 1000);

        // Get field count in structure
        count = meta_in.getFieldCount();
        count = count - 1;

        for (int i = 0; i <= count; i++) {
            Len = meta_in.getLength(i);
            End = Start + Len;
            strB.insert(Start, data_in.getValue(i));
            Start = End++;
        }
        sdata = strB.toString();
        return sdata;
    }

    public static StringBuilder ClearString1000(StringBuilder str, int Len) {
        // Sets all characters to space
        for (int i = 1; i <= Len; i++) {
            str.insert(i, " ");
        }
        return str;
    }

    public static void main(String[] args) throws JCoException {

        MyDestinationDataProvider myProvider = new MyDestinationDataProvider();

        //register the provider with the JCo environment;
        //catch IllegalStateException if an instance is already registered
        try {
            com.sap.conn.jco.ext.Environment.registerDestinationDataProvider(myProvider);
        } catch (IllegalStateException providerAlreadyRegisteredException) {
            //somebody else registered its implementation,
            //stop the execution
            throw new Error(providerAlreadyRegisteredException);
        }

        String destName = "ABAP_AS";
        iDocTest dest;

        dest = new iDocTest();

        //set properties for the destination and ...
        myProvider.changeProperties(destName, getDestinationPropertiesFromUI());

        //... work with it             
        iDocTest.iDocCall(destName);

        //Clear the properties and ...
        myProvider.changeProperties(destName, null);
    }
}

--------- End Java Code --------------

Actions

Filter Blog

By author:
By date:
By tag: