Technical Articles
Development process in SAP BTP Neo environment without the Java tools for Eclipse
As you might already know, as of 25 March 2021, the Java tools for Eclipse that work with the SAP BTP SDK for the Neo environment are no longer supported. As of 17 June 2021, the Java tools for Eclipse are removed from the SAP Development Tools site.
This means that if you were using the the Java tools for Eclipse, you will need to change your development process. From now on, you will use IDE of your choice and the console client that is part of the SAP BTP SDK for the Neo environment.
The following blog post will help you to get familiar with this development process. It is not a new process – it has always existed and some of you might already use it.
If you see a similar error message in Eclipse while trying to add a new cloud server, it is because the Java tools for Eclipse that work with the SAP BTP SDK for the Neo environment are no longer supported. They were available for installation and usage after end of support, until they stopped working properly.
If you are no longer able to install the Java tools for Eclipse that work with the SAP BTP SDK for the Neo environment, it is because they are removed from the SAP Development Tools site.
Development environment
Your development environment depends on what you want to achieve. Here is a sample table with actions and tools, which support these actions:
Working locally
Tools
Action | Tools |
Develop & build | – IDE & build tools of your choice – SAP BTP SDK |
Run | – Console client (part of SAP BTP SDK) |
Debug | – IDE of your choice – Console client (part of SAP BTP SDK) |
Profile | – Eclipse IDE (Oxygen or newer) – SAP JVM Tools for Eclipse – SAP JVM |
The local server
In order to run the application locally, you need to install a local SAP BTP server; this is done with the console client command neo install-local. By running this command, you install a server runtime in a local folder called server, which by default is located in your SAP BTP SDK installation directory.
In order to configure and manage your local application (e.g. add destination, get application logs, etc.), you might need to work directly with some files in the server folder.
Some of the more interesting files within the server folder are:
Purpose | Location | File |
Get logs | /server/log/ | |
Debug app |
/server/bin/
|
– setenv.sh – setenv.bat |
Add app authentication | /server/config_master/com.sap.security.um.provider.neo.local/ | – neogroups.json – neousers.json |
Manage destinations |
/server/config_master/service.destinations/destinations/
|
Note: the com.sap.security.um.provider.neo.local folder does not exist upon initial local server installation; you need to create the folder and required files inside if your scenario needs them.
Working on the cloud
Tools
Action | Tools |
Run | – Console client (part of BTP SDK) |
Debug | – Eclipse IDE (Oxygen or newer) – SAP JVM Tools for Eclipse |
Profile | – Eclipse IDE (Oxygen or newer) – SAP JVM Tools for Eclipse |
The cloud server
In the cloud scenario you do not have access to a server folder. You can configure and manage your application (e.g. add destination, get application logs) via console client commands.
Development process
Let’s go through a sample development process by building a simple Java web application.
Prerequisites
You can use IDE and build tools of your choice for developing the Java application. In the blog post, we will use Eclipse IDE and Maven.
We will also use Linux, Mac OS X, or other Unix based OS; if you are using Microsoft Windows – pay attention to the additional notes on each step.
- IDE of choice; for the purposes of the blog post: Eclipse IDE
- Build tool of choice; for the purposes of the blog post: Maven
- SAP BTP SDK; for the purposes of the blog post: Java Web Tomcat 8, version 3.129.5
Create and build Java web project
- Create new Java web project using Maven
mvn archetype:generate -DgroupId=com.sap.sample -DartifactId=sample-web-app -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
- Create PublicServlet.java file under /sample-web-app/src/main/java/com/sap/sample/
cd sample-web-app mkdir -p "./src/main/java/com/sap/sample/" touch "./src/main/java/com/sap/sample/PublicServlet.java"
- And update its contents
package com.sap.sample; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class PublicServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().println("<p>Hello!</p>"); } }
- Update the sample-web-app/src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://Java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>Hello!</display-name> <servlet> <servlet-name>PublicServlet</servlet-name> <servlet-class>com.sap.sample.PublicServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>PublicServlet</servlet-name> <url-pattern>/public</url-pattern> </servlet-mapping> </web-app>
- Add dependency to Servlet 3.1 API in the pom.xml
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
- Build the project using Maven
mvn clean install
Run the application locally
This is the documentation describing how to run applications locally.
We have developed and built the Java application. Let’s run it locally.
Procedure
- Run the project on a local SAP BTP server
# install local server; the server will be located in the <SDK installation folder>/server by default neo install-local # start local server neo start-local # deploy the sample application and check http://localhost:8080/sample-web-app/public neo deploy-local --source <path to the sample-web-app folder>/sample-web-app/target/sample-web-app.war
- Check that the application is running on http://localhost:8080/sample-web-app/public
Debug the application locally
This is the documentation describing how to debug applications locally.
We have developed, built and run the Java application locally. Let’s debug it locally.
Note: you can use IDE of your choice; for the purposes of the blog post we are using Eclipse IDE.
Prerequisites
- You should have already installed a local server and run the application on it.
Procedure
- Enable remote debugging of the local SAP BTP server: open /<SDK installation folder>/server/bin/setenv.sh and add in the end of the file:
CATALINA_OPTS="-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n"
Note for Microsoft Windows users: check documentation for this step.
- Restart the local SAP BTP server
# stop the local server neo stop-local # start the local server neo start-local
- Import the project in the IDE.
- Attach a remote Java debugger from within the IDE on localhost:5005
- Put a debug breakpoint on the PublicServlet.java doGet() method and hit http://localhost:8080/sample-web-app/public
Profile the application locally
This is the documentation describing how to profile applications locally.
We have run the Java web application locally. Let’s profile it with SAP JVM Profiler.
Note: if you want to profile your application using SAP JVM Profiler, you must use SAP JVM, SAP JVM Tools for Eclipse and thus Eclipse IDE (Oxygen or newer).
Procedure
Follow the procedure as described in the documentation.
Environment variables and system properties locally
We have a Java web application, which reads environment variables and system properties. Let’s run the Java web application locally and set the required environment variables and system properties.
Procedure for system properties
- Read the system property in your Java web application
System.getProperties().getProperty("custom.system.property")
- Add the system property in the end of the /<SDK installation folder>/server/props.ini file
-Dcustom.system.property=my.system.property
- Deploy the application locally and restart the local SAP BTP server if already started
Procedure for environment variables
- Read the environment variable in your Java web application
System.getenv("CUSTOM_ENVIRONMENT_VARIABLE")
- Add the environment variable in the end of the /<SDK installation folder>/server/bin/setenv.sh file
export CUSTOM_ENVIRONMENT_VARIABLE="MY_ENVIRONMENT_VARIABLE"
Note for Microsoft Windows users: add the environment variable in the /<SDK installation folder>/server/bin/setenv.bat file and using the set CUSTOM_ENVIRONMENT_VARIABLE=”MY_ENVIRONMENT_VARIABLE” syntax.
- Deploy the application locally and restart the local SAP BTP server if already started
Run the application on the cloud
This is the documentation describing how to run applications on the cloud.
We have developed and run the Java application locally. Let’s run it on the cloud.
Procedure
- Deploy the application on the cloud
neo deploy --host <host> --account <subaccount_name> --application <application_name> --source <path to the sample-web-app folder>/sample-web-app/target/sample-web-app.war --user <email_or_user>
Debug application on the cloud
This is the documentation describing how to debug applications on the cloud.
We have deployed the Java application on the cloud. Let’s debug it on the cloud.
Note: if you want to debug the application on the cloud, you must use SAP JVM Tools for Eclipse and thus Eclipse IDE (Oxygen or newer).
Procedure
Follow the procedure as described in the documentation.
Profile the application on the cloud
This is the documentation describing how to profile applications on the cloud.
We have deployed the Java application on the cloud. Let’s profile it on the cloud.
Note: if you want to profile the application on the cloud, you must use SAP JVM Tools for Eclipse and thus Eclipse IDE (Oxygen or newer).
Procedure
Follow the procedure as described in the documentation.
Environment variables and system properties on the cloud
We have a Java web application, which reads environment variables and system properties. Let’s set the environment variables and system properties and run the Java web application on the cloud.
Procedure for system properties
- Read the system property in your Java web application
System.getProperties().getProperty("custom.system.property")
- Deploy the application on the cloud and set the required system property via the “–vm-arguments” parameter
neo deploy --host <host> --account <subaccount_name> --application <application_name> --vm-arguments "-Dcustom.system.property=my.system.property" --source <path to the war file> --user <email_or_user>
Procedure for environment variables
- Read the environment variable in your Java web application
System.getenv("CUSTOM_ENVIRONMENT_VARIABLE")
- Deploy the application on the cloud and set the required environment variable via the “–ev” parameter
neo deploy --host <host> --account <subaccount_name> --application <application_name> --ev CUSTOM_ENVIRONMENT_VARIABLE=MY_ENVIRONMENT_VARIABLE --source <path to the war file> --user <email_or_user>
Use SAP BTP SDK API in the web project
Until now, we have developed a simple Java application, which does not take advantage of any SAP BTP APIs. Let’s change that.
Add SAP BTP API as Maven dependency
This is a blog post describing how to build Java web applications with Maven.
This are all SAP BTP artifacts on Maven Central.
- Add the SAP BTP SDK API as a dependency in the In the /sample-web-application/pom.xml
<dependency> <groupId>com.sap.cloud</groupId> <artifactId>neo-java-web-api</artifactId> <version>3.129.5</version> <scope>provided</scope> </dependency>
Note: the artifactId and version depend on the kind and version of the SAP BTP SDK.
Add destination locally
We have developed the Java application. Let’s add a destination in the existing servlet.
Note: when working locally, you must add all destinations as files with no extension in the /server/config_master/service.destinations/destinations/ folder.
- Add destination called outbound-internet-destination in the /server/config_master/service.destinations/destinations/ folder
Name=outbound-internet-destination URL=https\://help.sap.com/doc/8cd05d3af14a42f7a53c0609acf55826/Cloud/en-US/terms_of_use.html ProxyType=Internet Type=HTTP Authentication=NoAuthentication TrustAll=true CloudConnectorVersion = 2
- Update the PublicServlet.java code to locate the destination and print its properties
package com.sap.sample; import java.io.IOException; import javax.annotation.Resource; import javax.naming.Context; import javax.naming.InitialContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.sap.cloud.account.TenantContext; import com.sap.core.connectivity.api.configuration.ConnectivityConfiguration; import com.sap.core.connectivity.api.configuration.DestinationConfiguration; public class PublicServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Resource private TenantContext tenantContext; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // Look up the connectivity configuration API Context ctx = new InitialContext(); ConnectivityConfiguration configuration = (ConnectivityConfiguration) ctx .lookup("java:comp/env/connectivityConfiguration"); // Get destination configuration String destinationName = "outbound-internet-destination"; DestinationConfiguration destConfiguration = configuration.getConfiguration(destinationName); if (destConfiguration == null) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, String.format( "Destination %s is not found. Hint:" + " Make sure to have the destination configured.", destinationName)); return; } String url = destConfiguration.getProperty("URL"); String proxyType = destConfiguration.getProperty("ProxyType"); response.getWriter().println("<p>Destination: " + url + "</p><p>Proxy type: " + proxyType + "</p>"); } catch (Exception e) { // Connectivity operation failed String errorMessage = "Connectivity operation failed with reason: " + e.getMessage(); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorMessage); } } }
- Update the sample-web-app/src/main/webapp/WEB-INF/web.xml to declare the JNDI lookup for the destination:
<resource-ref> <res-ref-name>connectivityConfiguration</res-ref-name> <res-type>com.sap.core.connectivity.api.configuration.ConnectivityConfiguration</res-type> </resource-ref>
- Build the application, deploy it on the local server and request http://localhost:8080/sample-web-app/public. It should print the destination properties.
Add authentication locally
This is the documentation describing authentication in Neo environment.
We have developed the Java application. Let’s add a second servlet in it, which will be secured with a basic authentication.
- Create ProtectedServlet.java file under /sample-web-app/src/main/java/com/sap/sample/
package com.sap.sample; import java.io.IOException; import java.security.Principal; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.sap.security.um.service.UserManagementAccessor; import com.sap.security.um.user.PersistenceException; import com.sap.security.um.user.UnsupportedUserAttributeException; import com.sap.security.um.user.User; import com.sap.security.um.user.UserProvider; public class ProtectedServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { response.getWriter().println("<p>Welcome " + getUserAttributes(request.getUserPrincipal()) + "</p>"); } catch (Exception e) { response.getWriter().println("Protected operation failed with reason: " + e.getMessage()); } } /** * Get name and e-mail user attributes and return them as condensed string. */ private String getUserAttributes(Principal principal) throws PersistenceException, UnsupportedUserAttributeException { // Get user from user storage based on principal name UserProvider userProvider = UserManagementAccessor.getUserProvider(); User user = userProvider.getUser(principal.getName()); // Extract and return user name and e-mail address if present String firstName = user.getAttribute("firstname"); String lastName = user.getAttribute("lastname"); String eMail = user.getAttribute("email"); return (firstName != null && lastName != null ? firstName + " " + lastName + " [" + principal.getName() + "]" : principal.getName()) + (eMail != null ? " (" + eMail + ")" : ""); } }
- Add the second servlet and a security configuration in the sample-web-app/src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://Java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>Hello!</display-name> <servlet> <servlet-name>PublicServlet</servlet-name> <servlet-class>com.sap.sample.PublicServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>PublicServlet</servlet-name> <url-pattern>/public</url-pattern> </servlet-mapping> <servlet> <servlet-name>ProtectedServlet</servlet-name> <servlet-class>com.sap.sample.PublicServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ProtectedServlet</servlet-name> <url-pattern>/protected</url-pattern> </servlet-mapping> <!-- Secure application --> <login-config> <auth-method>BASIC</auth-method> </login-config> <security-constraint> <web-resource-collection> <web-resource-name>Protected Area</web-resource-name> <url-pattern>/protected/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>Everyone</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>Public Area</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> </security-constraint> <security-role> <description>All SAP BTP users</description> <role-name>Everyone</role-name> </security-role> </web-app>
- Add the neousers.json file in /server/config_master/com.sap.security.um.provider.neo.local/
The file contains two users with role Everyone: first user with id john and password johndoe, and second user jane with password janedoe.
Note: create the com.sap.security.um.provider.neo.local folder, if not already created.{ "Users": [ { "UID": "john", "Password": "{SSHA}Ub53I5XdyH/Nh3gr3pUQ6vPyjYPPRVKF", "Roles": [ "Everyone" ], "Attributes": [ { "attributeName": "firstname", "attributeValue": "John" }, { "attributeName": "lastname", "attributeValue": "Doe" }, { "attributeName": "email", "attributeValue": "john.doe@sap.com" } ] }, { "UID": "jane", "Password": "{SSHA}/obq802EmKc+rc4/fjU/XzbvBiaHtNIH", "Roles": [ "Everyone" ], "Attributes": [ { "attributeName": "firstname", "attributeValue": "Jane" }, { "attributeName": "lastname", "attributeValue": "Doe" }, { "attributeName": "email", "attributeValue": "jane.doe@sap.com" } ] } ] }
Note: SSHA is a password storage scheme, the salted version of the SHA-1. When developing locally, you can use the slappasswd utility – an OpenLDAP password utility for Linux, Mac OS X, or other Unix based OS, which generates such hashes: “slappasswd -h {SSHA} -s <password>“.
There are also few online web pages, which can generate LDAP SSH password hash; just have in mind that this approach is appropriate for testing purposes only. - Build the application, deploy it on the local server and request http://localhost:8080/sample-web-app/protected. It should ask you to log in; usernames and passwords are as follow: is john with password johndoe; jane with password janedoe.
Wrap up
In this blog post we were able to develop, run, debug, profile and configure an application locally and on the cloud without using the Java tools for Eclipse that work with the SAP BTP SDK for the Neo environment.
I hope the blog post helped you to set up and start the development process using the console client that is part of the SAP BTP SDK for the Neo environment.
Just read your blogpost. We have a problem with regards to tomcat 9. Can you verify that this also works with tomcat 9? We cannot get local authentication going with the NEO tomcat 9 SDK.
Hello Jaron,
yes, I can confirm that CLI tools are part of all Neo SDKs - including the Tomcat 9 based one.
Best Regards
Hi
Thanks for your post, but this is not a usable development process. Each simple code modification requires a complete build and deployment to a local server. I guess this approach requires two to four times more development effort. Isn't there a way for automatic publishing changes?
There can I find further documentation on this "new" development approach?
Thanks.
Regards
Karsten
Hello Karsten,
As far as I know no other option.
Best regards
Hi Snezhana,
for the local dev approach, how can I change the logging settings?
I've found a logback.xml in server\bin\logback-config. Is this the intended place to setup logging for local development? Is there a way to change logging settings for a running tomcat instance?
Thanks in advance!
Ralf
Hello Ralf,
as the runtime is basically a Tomcat server with some additional libraries, the approach for setting log levels in local Neo Server should be the same as local Tomcat server.
With google search about "tomcat change log level without restart" I found several articles that explain possible approach that involves Mean tab on JConsole.
I hope this will help.
Best regards