From: Instrumental Date: Fri, 7 Sep 2018 16:41:35 +0000 (-0500) Subject: Add Servlet Samples X-Git-Tag: 2.1.2~56 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=824dc7b5fc0e1ccdf7f460479aff344727f0f01e;hp=b8369a2668ad8f011d118de83a7b70f75f247332;p=aaf%2Fauthz.git Add Servlet Samples Issue-ID: AAF-472 Change-Id: I69aae6509d65340942c4dee718b5dfe7726aab04 Signed-off-by: Instrumental --- diff --git a/cadi/pom.xml b/cadi/pom.xml index 86c4b1f8..191dcacb 100644 --- a/cadi/pom.xml +++ b/cadi/pom.xml @@ -127,6 +127,7 @@ client aaf oauth-enduser + servlet-sample diff --git a/cadi/servlet-sample/.gitignore b/cadi/servlet-sample/.gitignore new file mode 100644 index 00000000..2c21dc69 --- /dev/null +++ b/cadi/servlet-sample/.gitignore @@ -0,0 +1,8 @@ +/target/ +/.settings/ +/META-INF/ +/.classpath +/.project +/logs/ +/run/ +/caditest.war diff --git a/cadi/servlet-sample/WEB-INF/.gitignore b/cadi/servlet-sample/WEB-INF/.gitignore new file mode 100644 index 00000000..8e4ce94c --- /dev/null +++ b/cadi/servlet-sample/WEB-INF/.gitignore @@ -0,0 +1 @@ +classes/ diff --git a/cadi/servlet-sample/WEB-INF/web.xml b/cadi/servlet-sample/WEB-INF/web.xml new file mode 100644 index 00000000..0b869fe6 --- /dev/null +++ b/cadi/servlet-sample/WEB-INF/web.xml @@ -0,0 +1,30 @@ + + + + MyServlet + org.onap.aaf.sample.cadi.MyServlet + + + + MyServlet + /testme + + + + CADI + org.onap.aaf.cadi.filter.CadiFilter + + cadi_prop_files + /opt/app/osaafE/client/etc/org.osaaf.aaf.client.props + + + + + CADI + /* + + + diff --git a/cadi/servlet-sample/build.sh b/cadi/servlet-sample/build.sh new file mode 100644 index 00000000..6880dbf2 --- /dev/null +++ b/cadi/servlet-sample/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +rm -Rf WEB-INF/classes/* +cp -Rf target/test-classes/* WEB-INF/classes +jar -cvf caditest.war META-INF WEB-INF index.html diff --git a/cadi/servlet-sample/index.html b/cadi/servlet-sample/index.html new file mode 100644 index 00000000..926497af --- /dev/null +++ b/cadi/servlet-sample/index.html @@ -0,0 +1,10 @@ + +
+ CADI Test Page +
+ +

Welcome to the CADI Test Page

+

This page is surrounded by a Tomcat Valve implementing CSP via TAF (CSSA) Framework

+ Click here to test the protected Servlet + + \ No newline at end of file diff --git a/cadi/servlet-sample/pom.xml b/cadi/servlet-sample/pom.xml new file mode 100644 index 00000000..5711d440 --- /dev/null +++ b/cadi/servlet-sample/pom.xml @@ -0,0 +1,93 @@ + + + org.onap.aaf.authz + cadiparent + .. + 2.1.2-SNAPSHOT + + 4.0.0 + CADI Servlet Sample (Test Only) + aaf-cadi-servlet-sample + jar + + + 8.5.23 + + + + + + org.eclipse.jetty + jetty-webapp + ${project.jettyVersion} + + + + org.apache.tomcat.embed + tomcat-embed-jasper + ${tomcat.version} + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} + + + org.apache.tomcat.embed + tomcat-embed-jasper + ${tomcat.version} + + + org.apache.tomcat + tomcat-jasper + ${tomcat.version} + + + org.apache.tomcat + tomcat-jasper-el + ${tomcat.version} + + + org.apache.tomcat + tomcat-jsp-api + ${tomcat.version} + + + + org.onap.aaf.authz + aaf-cadi-aaf + ${project.version} + + + + + + + + true + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + 2.4 + maven-jar-plugin + + target + + + true + + + + + + + + diff --git a/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/MyServlet.java b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/MyServlet.java new file mode 100644 index 00000000..466c02da --- /dev/null +++ b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/MyServlet.java @@ -0,0 +1,126 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.sample.cadi; + +import java.io.IOException; +import java.security.Principal; + +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import org.onap.aaf.cadi.aaf.v2_0.AAFCon; +import org.onap.aaf.cadi.client.Future; +import org.onap.aaf.cadi.principal.TaggedPrincipal; + +// Uncomment if you utilized the "MiniJASPIWrap" in the Servlet setup in "main()", and want to protect your service via Permission or mapped role +// @RolesAllowed({"com.att.aaf.myPerm|myInstance|myAction"}) + public class MyServlet implements Servlet { + private ServletConfig servletConfig; + + public void init(ServletConfig config) throws ServletException { + servletConfig = config; + } + + public ServletConfig getServletConfig() { + return servletConfig; + } + + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { + HttpServletRequest request; + try { + request = (HttpServletRequest)req; + } catch (ClassCastException e) { + throw new ServletException("Only serving HTTP today",e); + } + + res.getOutputStream().println("
CSP Servlet Test

You're good to go!

" +
+					request.getUserPrincipal());
+			
+			String perm = request.getParameter("PERM");
+			if(perm!=null) {
+				if(request.isUserInRole(perm)) {
+					if(perm.indexOf('|')<0) { 
+						res.getOutputStream().println("\nCongrats!, You are in Role " + perm);
+					} else { 
+						res.getOutputStream().println("\nCongrats!, You have Permission " + perm);
+					}
+				} else {
+					if(perm.indexOf('|')<0) { 
+						res.getOutputStream().println("\nSorry, you are NOT in Role " + perm);
+					} else {
+						res.getOutputStream().println("\nSorry, you do NOT have Permission " + perm);
+					}
+				}
+			}
+			
+			// You can get the working AAFCon from Trans
+			AAFCon aafcon = AAFCon.obtain(req);
+			if(aafcon!=null) {
+				try {
+					res.getOutputStream().println("----- Perms JSON from direct call -----");
+					final Principal up = request.getUserPrincipal();
+					TaggedPrincipal tp;
+					if(up instanceof TaggedPrincipal) {
+						tp = (TaggedPrincipal)up;
+					} else {
+						tp = new TaggedPrincipal() {
+							@Override
+							public String getName() {
+								return up.getName();
+							}
+
+							@Override
+							public String tag() {
+								return "Unknown";
+							}
+						};
+					}
+					// This call will be "as the user calling", but only if permission is set to trust.
+//					Future future = aafcon.clientAs("2.0",tp).read("/authz/perms/user/"+request.getUserPrincipal().getName(),"application/Perms+json");
+					Future future = aafcon.client("2.0").read("/authz/perms/user/"+request.getUserPrincipal().getName(),"application/Perms+json");
+					if(future.get(4000 /* timeout */)) {
+						res.getOutputStream().print(future.value);
+					} else {
+						System.err.println(future.code() + ", " + future.body());
+						res.getOutputStream().print(future.code() + ", " + future.body());
+					}
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			} else {
+				res.getOutputStream().println("No AAFCon instantiated");
+			}
+			res.getOutputStream().print("
"); + + } + + public String getServletInfo() { + return "MyServlet"; + } + + public void destroy() { + } + } \ No newline at end of file diff --git a/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/Config.java b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/Config.java new file mode 100644 index 00000000..17f9b198 --- /dev/null +++ b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/Config.java @@ -0,0 +1,45 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.sample.cadi.jetty; + +import java.util.EnumSet; + +import javax.servlet.DispatcherType; + +import org.eclipse.jetty.webapp.WebAppContext; +import org.onap.aaf.cadi.filter.CadiFilter; + + + +public class Config { + /** + * Method to make jetty configurations (others?) with more complex function possible + * + * @param sc + */ + public static final void addToContext(WebAppContext sc, String propFile) { + sc.addFilter(CadiFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST)); + sc.setInitParameter(org.onap.aaf.cadi.config.Config.CADI_PROP_FILES, propFile); + } + + +} diff --git a/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/JettyServletServer.java b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/JettyServletServer.java new file mode 100644 index 00000000..2d1fc097 --- /dev/null +++ b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/JettyServletServer.java @@ -0,0 +1,112 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.sample.cadi.jetty; + +import java.net.Inet4Address; +import java.util.concurrent.ArrayBlockingQueue; + +import javax.servlet.Servlet; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.FilterMapping; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.PropAccess; +import org.onap.aaf.cadi.config.Config; +import org.onap.aaf.cadi.config.SecurityInfo; +import org.onap.aaf.cadi.filter.CadiFilter; + +public abstract class JettyServletServer implements Servlet { + + public static Server run(PropAccess access, String context, Class servletCls, int port, String ...args) throws Exception { + // Defaults: + int blockingQueueSize = 10; + int corePoolSize = 10; + int maxPoolSize = 10; + int keepAliveTime = 3000; + String hostname = access.getProperty(Config.HOSTNAME, null); + if(hostname==null) { + hostname = Inet4Address.getLocalHost().getHostName(); + } + + // Add your own Properties to override defaults + + ArrayBlockingQueue queue = new ArrayBlockingQueue(blockingQueueSize); + QueuedThreadPool pool = new QueuedThreadPool(maxPoolSize,corePoolSize,keepAliveTime,queue); + Server server = new Server(pool); + + String protocol; + if(access.getProperty(Config.CADI_KEYSTORE_PASSWORD,null)==null) { + ServerConnector conn = new ServerConnector(server); + conn.setHost(hostname); + conn.setPort(port); + server.addConnector(conn); + protocol = "http"; + } else { + // Setup Security + SecurityInfo securityInfo = new SecurityInfo(access); + SslContextFactory scf = new SslContextFactory(); + scf.setSslContext(securityInfo.getSSLContext()); + scf.setWantClientAuth(true); + ServerConnector sslConnector = new ServerConnector(server,scf); + sslConnector.setHost(hostname); + sslConnector.setPort(port); + server.addConnector(sslConnector); + protocol = "https"; + } + + // Setup Sample Servlet + CadiFilter cf = new CadiFilter(true,access); + FilterHolder cfh = new FilterHolder(cf); + + ServletHandler shand = new ServletHandler(); + shand.addFilterWithMapping(cfh, "/*", FilterMapping.ALL); + // To use normal Servlets, just add the class here... Actually, bug in Jetty... need to add with ServletHolder + ServletHolder sh = new ServletHolder(); + sh.setServlet(servletCls.newInstance()); + shand.addServletWithMapping(sh,"/*"); + + // To use JASPI Authorization Style to protect the servlet, wrap the Servlet + // with the "MiniJSAPIWrap class, as shown here. Then add "@RolesAllowed" on your + // servlet (see sample). Use Pipe delimited Permissions, not AAF Roles in the line + // shand.addServletWithMapping(new MiniJASPIWrap(MyServlet.class),"/*"); + // call initialize after start + ContextHandler ch = new ServletContextHandler(); + ch.setContextPath(context); + ch.setHandler(shand); + server.setHandler(ch); + // Startup the Server + server.setStopAtShutdown(true); + server.start(); + + access.log(Level.INFO,"TestServlet is running at " + protocol + "://"+hostname+':'+port+context); + return server; + } + +} diff --git a/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/JettyStandalone.java b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/JettyStandalone.java new file mode 100644 index 00000000..049e45e8 --- /dev/null +++ b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/JettyStandalone.java @@ -0,0 +1,46 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.sample.cadi.jetty; + +import org.eclipse.jetty.server.Server; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.sample.cadi.MyServlet; +import org.onap.aaf.cadi.PropAccess; + + + + +public class JettyStandalone { + public static void main(String[] args) { + PropAccess access = new PropAccess(args); + try { + Server server = JettyServletServer.run(access, "/caditest", MyServlet.class, 3456); + server.join(); + } catch (Exception e) { + access.log(Level.ERROR, e); + } finally { + access.log(Level.INFO,"Stopping Service"); + } + + } + +} diff --git a/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/MiniJASPIWrap.java b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/MiniJASPIWrap.java new file mode 100644 index 00000000..904f3cdc --- /dev/null +++ b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/jetty/MiniJASPIWrap.java @@ -0,0 +1,101 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.sample.cadi.jetty; + +import java.io.IOException; + +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.UnavailableException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.servlet.ServletHolder; +import org.onap.aaf.cadi.filter.RolesAllowed; + + + +/** + * MiniJASPIWrap + * + * Support the ability to check JASPI Annotation Style Authorizations. + * + * This can be a clean way to enforce API Authorization without mistakes in code. + * + * @author JonathanGathman + * + */ +public class MiniJASPIWrap extends ServletHolder { + private RolesAllowed rolesAllowed; + //private String roles; + public MiniJASPIWrap(Class servlet) { + super(servlet); + this.rolesAllowed = servlet.getAnnotation(RolesAllowed.class); + StringBuilder sb = new StringBuilder(); + boolean first = true; + if(rolesAllowed!=null) { + for(String str : rolesAllowed.value()) { + if(first)first=false; + else sb.append(','); + sb.append(str); + } + } + //roles = sb.toString(); + } + + /** + * handle + * + * When utilized, this class authorizes the transaction by first calling the standard J2EE API call + * "isUserInRole" with the role(s) found in the class Annotations (JASPI Style) + */ + @Override + public void handle(Request baseRequest, ServletRequest request, ServletResponse response) throws ServletException, UnavailableException, IOException { + if(rolesAllowed==null) { + super.handle(baseRequest, request, response); + } else { // Validate + try { + + HttpServletRequest hreq = (HttpServletRequest)request; + boolean proceed = false; + for(String role : rolesAllowed.value()) { + if(hreq.isUserInRole(role)) { + proceed = true; + break; + } + } + if(proceed) { + super.handle(baseRequest, request, response); + } else { + //baseRequest.getServletContext().log(hreq.getUserPrincipal().getName()+" Refused " + roles); + ((HttpServletResponse)response).sendError(403); // forbidden + } + } catch(ClassCastException e) { + throw new ServletException("JASPIWrap only supports HTTPServletRequest/HttpServletResponse"); + } + } + } + +} diff --git a/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/tomcate/TomcatEmbedded.java b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/tomcate/TomcatEmbedded.java new file mode 100644 index 00000000..415283a5 --- /dev/null +++ b/cadi/servlet-sample/src/test/java/org/onap/aaf/sample/cadi/tomcate/TomcatEmbedded.java @@ -0,0 +1,108 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.sample.cadi.tomcate; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; + +import org.apache.catalina.Service; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.startup.Tomcat; +import org.apache.log4j.chainsaw.Main; +import org.onap.aaf.cadi.Access; +import org.onap.aaf.cadi.Access.Level; +import org.onap.aaf.cadi.PropAccess; + +/** + * @author JonathanGathman + * + */ +public class TomcatEmbedded { + + public static void main(String[] args) throws Exception { + System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true"); + Tomcat tomcat = new Tomcat(); + + Service service = tomcat.getService(); + service.addConnector(getSslConnector(new PropAccess(args), 8081)); + + tomcat.addWebapp("/caditest", getRootFolder().getAbsolutePath()); + + tomcat.start(); + tomcat.getServer().await(); + + } + + private static Connector getSslConnector(PropAccess access, int port) throws IOException { + Connector connector = new Connector(); + connector.setPort(port); + connector.setSecure(true); + connector.setScheme("https"); + setAttr(connector,access,"keyAlias","cadi_alias"); + setAttr(connector,access,"keystoreFile","cadi_keystore"); + connector.setAttribute("keystoreType", "PKCS12"); + setAttr(connector,access,"keystorePass","cadi_keystore_password"); + setAttr(connector,access,"truststoreFile","cadi_truststore"); + connector.setAttribute("truststoreType", "JKS"); + setAttr(connector,access,"truststorePass","cadi_truststore_password"); + connector.setAttribute("clientAuth", "want"); + connector.setAttribute("protocol", "HTTP/1.1"); + connector.setAttribute("sslProtocol", "TLS"); + connector.setAttribute("maxThreads", "200"); + connector.setAttribute("protocol", "org.apache.coyote.http11.Http11AprProtocol"); + connector.setAttribute("SSLEnabled", true); + return connector; + } + + private static void setAttr(Connector connector, Access access, String ctag, String atag) throws IOException { + String value = access.getProperty(atag, null); + if(value==null) { + access.log(Level.ERROR, atag, "is null"); + } else { + if(value.startsWith("enc:")) { + access.log(Level.INIT,atag,"=enc:************"); + value = access.decrypt(value, false); + } else { + access.log(Level.INIT,atag,"=",value); + } + connector.setAttribute(ctag, value); + } + } + + private static File getRootFolder() { + try { + File root; + String runningJarPath = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath().replaceAll("\\\\", "/"); + int lastIndexOf = runningJarPath.lastIndexOf("/target/"); + if (lastIndexOf < 0) { + root = new File(""); + } else { + root = new File(runningJarPath.substring(0, lastIndexOf)); + } + System.out.println("application resolved root folder: " + root.getAbsolutePath()); + return root; + } catch (URISyntaxException ex) { + throw new RuntimeException(ex); + } + } +}