Spring boot app recouse is not found when docarize - spring-boot

My app works fine from the IDE. But when I docarize the app, it can't find the applicaiton resoruce. How to docarize the app correctly that I can access resocur folder. Here is my code:-
Dockerfile
FROM eclipse-temurin:17-jre-alpine
RUN mkdir -p /app
COPY target/Toolkit-*.jar /app/toolkit.jar
WORKDIR /app/
ENTRYPOINT java -jar toolkit.jar
I am reading propety as this:
#Log
public class PropertyLoader {
private static String RESOURCE_FILENAME = "application.properties";
private static PropertyLoader instance;
private Properties properties;
private PropertyLoader() {
// works fine form IDE. Docker inputStream is null
try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(RESOURCE_FILENAME)) {
properties = new Properties();
properties.load(inputStream);
} catch (IOException e) {
log.log(Level.SEVERE, e.getMessage());
}
}
}
How do I read application resorces in docker continaer?

You need to application.properties to the image as well. add this to your Dockerfile
RUN mkdir -p /app/config
COPY <your file> /app/config/application.properties

Related

Upload file to ec2 docker instance from spring boot

I have written a spring boot application rest-api to upload file on docker instance inside ec2.
Though I am not getting any errors, the file is not being uploaded to docker container.
#PostMapping(value = "/uploadFile")
public void uploadFile(#RequestPart(name = "file") MultipartFile file)
{
try {
System.out.println("Writing file");
byte[] bytes = file.getBytes();
Path path =Paths.get(uploadPath +file.getOriginalFilename());
System.out.println("Uploaded to :"+uploadPath+file.getOriginalFilename());
Files.write(path, file.getBytes());
}
catch (Exception e) {
System.out.println(e);
}
}
filepath = /home/ubuntu/dockerproject/file-upload/
What I want to achieve is that using the above rest api , the file should get uploaded to file-path location.
I am not able to rectify if the path is wrong or some other issue exists.
Also file-upload has permission 0777.

Unable to delete jetty working directory before jetty starts or creates it's working directory

I am kind of running into this interesting problem. I wanted to delete the jetty working directory before jetty restarts next time. The application has 2 war files and this is implemented using embed jetty. Name of the working folder looks like below:
jetty-0_0_0_0-10000-oaxui_war-_ui_oax-any-4214704288653178451
jetty-0_0_0_0-10000-oaxservice_war-_api_oax-any-1938823993160354573
Options 1: We have run.sh file which actually starts JettyServer.So I thought to place the below code in the file just before that.
echo "now deleting"
DELTEMP="rm -rf /tmp/jetty*"
exec $DELTEMP
echo "deleted....."
Result: It actually deletes the jetty working directory but does not let it create one working directory also.
Option 2: Create a temp directory in the location provided by us as below and then later delete this folder using the run.sh and above command but the path will be custom. Unfortunately, this also didn't help as the working directory was not being created in the first place.
private HandlerCollection getWebAppHandlers() throws SQLException, NamingException{
//Setting the war and context path for the service layer: oaxservice
File tempDir1 = new File("/faw/service/appshell/tempdir/");
File tempDir2 = new File("/faw/service/appshell/tempdir/");
WebAppContext serviceWebapp = new WebAppContext();
tempDir1.mkdirs();
serviceWebapp.setTempDirectory(tempDir1);
serviceWebapp.setWar(APPSHELL_API_WAR_FILE_PATH);
serviceWebapp.setContextPath(APPSHELL_API_CONTEXT_PATH);
//setting the war and context path for the UI layer: oaxui
WebAppContext uiWebapp = new WebAppContext();
tempDir2.mkdirs();
uiWebapp.setTempDirectory(tempDir2);
uiWebapp.setWar(APPSHELL_UI_WAR_FILE_PATH);
uiWebapp.setContextPath(APPSHELL_UI_CONTEXT_PATH);
uiWebapp.setAllowNullPathInfo(true);
uiWebapp.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
//set error page handler for the UI context
uiWebapp.setErrorHandler(new CustomErrorHandler());
//handling the multiple war files using HandlerCollection.
HandlerCollection handlerCollection = new HandlerCollection();
handlerCollection.setHandlers(new Handler[]{serviceWebapp, uiWebapp});
return handlerCollection;
}
Below is the complete JettyServer.java file
public class JettyServer {
private final static Logger logger = Logger.getLogger(JettyServer.class.getName());
private static final int JETTY_PORT = 10000;
private static final String JETTY_REALM_PROPERTIES_FILE_NAME = "realm.properties";
private static final String JETTY_REALM_NAME = "myrealm";
private static final String APPSHELL_WAR_FOLDER = "/faw/service/appshell/target/";
private static final String APPSHELL_UI_WAR_FILE_PATH = APPSHELL_WAR_FOLDER+"oaxui.war";
private static final String APPSHELL_API_WAR_FILE_PATH = APPSHELL_WAR_FOLDER+"oaxservice.war";
private static final String APPSHELL_API_CONTEXT_PATH = "/api/oax";
private static final String APPSHELL_UI_CONTEXT_PATH = "/ui/oax";
private static final String JETTY_CONFIG_FOLDER = "/faw/service/appshell/config/";
private static final String JETTY_CONFIG_FILE = JETTY_CONFIG_FOLDER+"datasource.properties";
private static final String JETTY_CONFIG_DATASOURCE_URL = "datasource.url";
private static final String JETTY_CONFIG_APPSHELL_SCHEMA_NAME = "appshell.datasource.username";
private static final String JETTY_CONFIG_APPSHELL_SCHEMA_PWD = "appshell.datasource.password";
private static final String JETTY_CONFIG_APPSHELL_DS_INITIAL_POOL_SIZE = "appshell.datasource.initialPoolSize";
private static final String JETTY_CONFIG_APPSHELL_DS_MAX_POOL_SIZE = "appshell.datasource.maxPoolSize";
private static final String JETTY_CONFIG_FAWCOMMON_SCHEMA_NAME = "fawcommon.datasource.username";
private static final String JETTY_CONFIG_FAWCOMMON_SCHEMA_PWD = "fawcommon.datasource.password";
private static final String JETTY_CONFIG_FAWCOMMON_DS_INITIAL_POOL_SIZE = "fawcommon.datasource.initialPoolSize";
private static final String JETTY_CONFIG_FAWCOMMON_DS_MAX_POOL_SIZE = "fawcommon.datasource.maxPoolSize";
private static final int INITIAL_POOL_SIZE_DEFAULT = 0;
private static final int MAX_POOL_SIZE_DEFAULT = 50;
private static final String JNDI_NAME_FAWAPPSHELL = "jdbc/CXOMetadataDatasource";
private static final String JNDI_NAME_FAWCOMMON = "jdbc/FawCommonDatasource";
private static final String JETTY_REQUEST_LOG_FILE_NAME = "/faw/logs/appshell/applogs/jetty-request.yyyy_mm_dd.log";
private static final String JETTY_REQUEST_LOG_FILE_NAME_DATE_FORMAT = "yyyy_MM_dd";
private static final boolean JETTY_REQUEST_LOG_FILE_APPEND = true;
private static final int JETTY_REQUEST_LOG_FILE_RETAIN_DAYS = 31;
private static final String JETTY_STDOUT_LOG_FILE_NAME = "/faw/logs/appshell/applogs/jetty-out.yyyy_mm_dd.log";
private static final int MAX_REQUEST_HEADER_SIZE = 65535;
private static final String SSL_SERVER_KEY_STROKE_PATH="/faw/tmp/customscript/certs/server.jks";
private static final String SSL_TRUST_KEY_STROKE_PATH="/faw/tmp/customscript/certs/trust.jks";
private static final String SSL_KEY_STROKE_KEY="changeit";
public static QueuedThreadPool threadPool;
public JettyServer() {
try {
//Redirect system out and system error to our print stream.
RolloverFileOutputStream os = new RolloverFileOutputStream(JETTY_STDOUT_LOG_FILE_NAME, true);
PrintStream logStream = new PrintStream(os);
System.setOut(logStream);
System.setErr(logStream);
Server server = new Server(JETTY_PORT);
server.addBean(getLoginService());
//Set SSL context
if (isIDCSEnvironment) {
try {
logger.info("Configuring Jetty SSL..");
HttpConfiguration http_config = new HttpConfiguration();
http_config.setSecureScheme("https");
http_config.setSecurePort(JETTY_PORT);
SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(SSL_SERVER_KEY_STROKE_PATH);
sslContextFactory.setCertAlias("server");
sslContextFactory.setKeyStorePassword(SSL_KEY_STROKE_KEY);
sslContextFactory.setTrustStorePath(SSL_TRUST_KEY_STROKE_PATH);
sslContextFactory.setTrustStorePassword(SSL_KEY_STROKE_KEY);
HttpConfiguration https_config = new HttpConfiguration(http_config);
https_config.addCustomizer(new SecureRequestCustomizer());
ServerConnector https = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(https_config));
https.setPort(JETTY_PORT);
server.setConnectors(new Connector[]{https});
logger.info("Jetty SSL successfully configured..");
} catch (Exception e){
logger.severe("Error configuring Jetty SSL.."+e);
throw e;
}
}
Configuration.ClassList classlist = Configuration.ClassList.setServerDefault(server);
classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.plus.webapp.EnvConfiguration",
"org.eclipse.jetty.plus.webapp.PlusConfiguration");
//register oaxui and oaxservice web apps
HandlerCollection webAppHandlers = getWebAppHandlers();
for (Connector c : server.getConnectors()) {
c.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setRequestHeaderSize(MAX_REQUEST_HEADER_SIZE);
c.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
}
threadPool = (QueuedThreadPool) server.getThreadPool();
// request logs
RequestLogHandler requestLogHandler = new RequestLogHandler();
AsyncRequestLogWriter asyncRequestLogWriter = new AsyncRequestLogWriter(JETTY_REQUEST_LOG_FILE_NAME);
asyncRequestLogWriter.setFilenameDateFormat(JETTY_REQUEST_LOG_FILE_NAME_DATE_FORMAT);
asyncRequestLogWriter.setAppend(JETTY_REQUEST_LOG_FILE_APPEND);
asyncRequestLogWriter.setRetainDays(JETTY_REQUEST_LOG_FILE_RETAIN_DAYS);
asyncRequestLogWriter.setTimeZone(TimeZone.getDefault().getID());
requestLogHandler.setRequestLog(new AppShellCustomRequestLog(asyncRequestLogWriter));
webAppHandlers.addHandler(requestLogHandler);
StatisticsHandler statisticsHandler = new StatisticsHandler();
statisticsHandler.setHandler(new AppshellStatisticsHandler());
webAppHandlers.addHandler(statisticsHandler);
// set handler
server.setHandler(webAppHandlers);
//start jettyMetricsPsr
JettyMetricStatistics.logJettyMetrics();
// set error handler
server.addBean(new CustomErrorHandler());
// GZip Handler
GzipHandler gzip = new GzipHandler();
server.setHandler(gzip);
gzip.setHandler(webAppHandlers);
//setting server attribute for datasources
server.setAttribute("fawappshellDS", new Resource(JNDI_NAME_FAWAPPSHELL, DatasourceUtil.getFawAppshellDatasource()));
server.setAttribute("fawcommonDS", new Resource(JNDI_NAME_FAWCOMMON, DatasourceUtil.getCommonDatasource()));
//new Resource(server, JNDI_NAME_FAWAPPSHELL, getFawAppshellDatasource());
//new Resource(server, JNDI_NAME_FAWCOMMON, getFawCommonDatasource());
Map<String, String> configDetails;
if (isIDCSEnvironment) {
configDetails = DatasourceUtil.getConfigMap();
} else {
configDetails = DatasourceUtil.configMapInternal;
}
if (isIDCSEnvironment && configDetails.containsKey(PODDB_ATP_ENABLED) && BooleanUtils.toBoolean(configDetails.get(PODDB_ATP_ENABLED))){
configDetails.put(DatabagProperties.LCM_MASTER_PROPERTY,"true");
try {
DBUtils.migrateDBATP(configDetails, DatasourceUtil.getFawAppshellDatasourceATP());
configDetails.remove(DatabagProperties.LCM_MASTER_PROPERTY, "true");
} catch(SQLException e){
logger.info("Exception while executing DBUtils.migrateDBATP.");
if(LCMUtils.isATPDBDown(e)) {
logger.info("Redis flow starts....Skipping consumption from Redis for now");
}
else{
logger.info("This is not eligible for redis flow. Actual Exception : "+ e);
throw e;
}
}
} else if (isIDCSEnvironment && configDetails.containsKey(PODDB_ATP_ENABLED) && !BooleanUtils.toBoolean(configDetails.get(PODDB_ATP_ENABLED))){
try{
DBUtils.migrateDB();
} catch (FawLCMPluginException e) {
logger.info(" This is not eligible for Redis consumption and exception is : " + e);
}
}
//For Dev env..
if (!isIDCSEnvironment && configDetails.containsKey(PODDB_ATP_ENABLED) && BooleanUtils.toBoolean(configDetails.get(PODDB_ATP_ENABLED))){
configDetails.put(DatabagProperties.LCM_MASTER_PROPERTY,"true");
try{
DBUtils.migrateDBATP(configDetails,DatasourceUtil.getDevFawAppshellDatasourceATP());
configDetails.remove(DatabagProperties.LCM_MASTER_PROPERTY,"true");
}
catch(SQLException e){
logger.info("Exception while executing DBUtils.migrateDBATP for dev environment.");
if(LCMUtils.isATPDBDown(e)) {
logger.info("Redis flow starts....Skipping consumption from Redis for now");
} else{
logger.info("This is not eligible for redis flow. Actual Exception for dev: "+ e);
}
}
} else if (!isIDCSEnvironment && configDetails.containsKey(PODDB_ATP_ENABLED) && !BooleanUtils.toBoolean(configDetails.get(PODDB_ATP_ENABLED))){
try {
DBUtils.migrateDB();
}catch (FawLCMPluginException e) {
logger.info(" This is not eligible for Redis consumption and exception in dev is : " + e);
}
}
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
}
}
private HandlerCollection getWebAppHandlers() throws SQLException, NamingException{
//Setting the war and context path for the service layer: oaxservice
File tempDir1 = new File("/faw/service/appshell/tempdir/");
File tempDir2 = new File("/faw/service/appshell/tempdir/");
WebAppContext serviceWebapp = new WebAppContext();
tempDir1.mkdirs();
serviceWebapp.setTempDirectory(tempDir1);
serviceWebapp.setWar(APPSHELL_API_WAR_FILE_PATH);
serviceWebapp.setContextPath(APPSHELL_API_CONTEXT_PATH);
//setting the war and context path for the UI layer: oaxui
WebAppContext uiWebapp = new WebAppContext();
tempDir2.mkdirs();
uiWebapp.setTempDirectory(tempDir2);
uiWebapp.setWar(APPSHELL_UI_WAR_FILE_PATH);
uiWebapp.setContextPath(APPSHELL_UI_CONTEXT_PATH);
uiWebapp.setAllowNullPathInfo(true);
uiWebapp.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
//set error page handler for the UI context
uiWebapp.setErrorHandler(new CustomErrorHandler());
//handling the multiple war files using HandlerCollection.
HandlerCollection handlerCollection = new HandlerCollection();
handlerCollection.setHandlers(new Handler[]{serviceWebapp, uiWebapp});
return handlerCollection;
}
/**
* The name of the LoginService needs to correspond to what is configured a webapp's web.xml which is
* <realm-name>myrealm</realm-name> and since it has a lifecycle of its own, we register it as a bean
* with the Jetty server object so it can be started and stopped according to the lifecycle of the server itself.
*
* #return the login service instance
* #throws FileNotFoundException In case realmProps is null
*/
public LoginService getLoginService() throws IOException {
URL realmProps = JettyServer.class.getClassLoader().getResource(JETTY_REALM_PROPERTIES_FILE_NAME);
if (realmProps == null)
throw new FileNotFoundException("Unable to find " + JETTY_REALM_PROPERTIES_FILE_NAME);
return new HashLoginService(JETTY_REALM_NAME, realmProps.toExternalForm());
}
public static void main(String[] args) {
new JettyServer();
}
}
Run.sh file :
#!/bin/bash
set -eu # Exit on error
set -o pipefail # Fail a pipe if any sub-command fails.
VERSION=1.0
cd "$(dirname "$0")"
RED='\033[0;31m'
ORANGE='\033[0;33m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color
function die() {
printf "${RED}%s\n${NC}" "$1" >&2
exit 1
}
function warn() {
printf "${ORANGE}%s\n${NC}" "$1"
}
function info() {
printf "${GREEN}%s\n${NC}" "$1"
}
CURR_PID=$$
mem_args=8192
info "mem args "$mem_args
export MAX_MEM=$mem_args
#put the managed server specific variable setting below, then export the variables
USER_MEM_ARGS="-Xms4096m -Xmx"$MAX_MEM"m -XX:MetaspaceSize=1024M -XX:MaxMetaspaceSize=1024m"
export USER_MEM_ARGS
info "USER_MEM_ARGS= ${USER_MEM_ARGS}"
#FAW_JAVA_OPTIONS="-Djava.util.logging.config.file=/faw/service/appshell/config/ucp_log.properties -Doracle.jdbc.fanEnabled=false $USER_MEM_ARGS -Xloggc:/faw/logs/appshell/applogs/gc.jetty.log -XX:+HeapDumpOnOutOfMemoryError -XshowSettings:vm -XX:+PrintCodeCache -XX:ReservedCodeCacheSize=512M -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=maxage=30m,defaultrecording=true,stackdepth=1024,dumponexit=true,dumponexitpath=/faw/logs/jetty/jetty_1618996595.jfr -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:+ExitOnOutOfMemoryError -XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=4 -XX:GCLogFileSize=5M -XX:HeapDumpPath=/faw/logs/appshell_hprof-dumps_`date +%x_%r|awk -F" " '{print $1}'|sed 's/\//_/g'`.hprof"
FAW_JAVA_OPTIONS="-Djava.util.logging.config.file=/faw/service/appshell/config/ucp_log.properties -Doracle.jdbc.fanEnabled=false $USER_MEM_ARGS -Xloggc:/faw/logs/appshell/applogs/gc.jetty.log -XX:+HeapDumpOnOutOfMemoryError -XshowSettings:vm -XX:+PrintCodeCache -XX:ReservedCodeCacheSize=512M -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:+ExitOnOutOfMemoryError -XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=4 -XX:GCLogFileSize=5M -XX:HeapDumpPath=/faw/logs/appshell_hprof-dumps_`date +%x_%r|awk -F" " '{print $1}'|sed 's/\//_/g'`.hprof"
echo "now deleting"
DELTEMP="rm -rf /tmp/jetty*"
exec $DELTEMP
echo "deleted....."
info "FAW_JAVA_OPTIONS= ${FAW_JAVA_OPTIONS}"
if [[ -e "/faw/environment" ]]; then
sh /faw/tmp/customscript/setUpTLSCerts.sh
fi
CMD="java ${FAW_JAVA_OPTIONS} -cp /faw/service/appshell/target:/faw/service/appshell/libs/* oracle.biapps.cxo.docker.jetty.server.JettyServer --lib /faw/service/appshell/libs"
ulimit -c 0
# Now execute it
info "Executing: $CMD"
exec $CMD
Any help is appreciated. Thank you.
Thank you "Joakim Erdfelt" for the input. Got it. The below solution works fine for me in embedded jetty. I will be creating a custom delete method for deleting the directory first and then creating the jetty working directory.
public void cleanJettyWorkingDirectory(){
final File folder = new File(JETTY_TEMP_WORKING_DIRECTORY);
final File[] files = folder.listFiles((dir, name) -> name.matches( "oax.*" ));
if (files!=null && files.length > 0) {
for ( final File file : files ) {
try {
FileUtils.deleteDirectory(file);
logger.info("Cleaning of the directory is completed.");
} catch (IOException e) {
logger.info("Unable to delete the Jetty working directory");
}
}
}else{
logger.info("No working directory file is present starting with oax for deletion.");
}
}
Also while setting the war , we will do below to create directories:
try {
cleanJettyWorkingDirectory();
logger.info("starting to create temp working directory for oax service ");
java.nio.file.Path oaxServicePath = Files.createTempDirectory("oaxservice");
String oaxServiceTempPath = oaxServicePath.toString();
logger.info("starting to create temp working directory for oax ui ");
java.nio.file.Path uiPath = Files.createTempDirectory("oaxui");
String oaxUIServiceTempPath = uiPath.toString();
File oaxServiceTempDir = new File(oaxServiceTempPath);
File oaxUITempDir = new File(oaxUIServiceTempPath);
serviceWebapp.setTempDirectory(oaxServiceTempDir);
uiWebapp.setTempDirectory(oaxUITempDir);
logger.info("The process of creating working directory is completed.");
} catch (IOException e) {
logger.log(Level.SEVERE, "Exception while creating directories.",e);
}

Cannot access docker container app by localhost, access by IP got timeout

I just got a new PC last week. So I setup my working environment as usual in Windows 10 with the latest Windows Docker Desktop. Then created a very simple spring boot REST service just to say hello, created the image with Spring boot Buildpacks 3 days ago, it worked fine with port mapping “docker run -p 8090:8080 davy/myapp”. This image is working well even today: I can access my application by “http://localhost:8090/sayHello” even today.
the working image
So, I started to build my real application and completed some functionalities. I wanted to test my app and created a new image by using spring boot Buildpacks.
Now I got a big problem: I cannot not access the application running in the container by port mapping with port mapping “docker run -p 8090:8080 davy/myapp” any more by “http://localhost:8090/sayHello”. It got an error page said "localhost did not send any data"
cannot send data image
Then I got my container IP by “docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 548e29f46ca7”, which displayed as “172.17.0.2/”. So I tried http://172.17.0.2:8090/sayHello. Now I got a timeout after waiting for some time I got "172.17.0.2 took too long to respond":
timeout image
I did not see any difference in the ports binding: both are 0.0.0.0:8090->8080/tcp
port binding for 2 images
I re-built the image several times by using Spring boot Buildpacks, 1 time Dockerfile and docker-compose.yml, and I cannot make the container like the old container any more.
I also tried “docker run -p 8088:8080 davyhu/myapp -m http.server --bind 0.0.0.0”, but got the same result: cannot access app by localhost, and IP timeout.
Thanks in advance for the helps!
Here are some more information:
config in pom.xml for buildpacks (no change for both versions in the pom.xml):
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>davyhu/${project.artifactId}</name>
</image>
</configuration>
</plugin>
</plugins>
</build>
PDFController.java
public class PdfGenerationController {
private static final Logger logger = LoggerFactory.getLogger(PdfGenerationController.class);
private static final SimpleDateFormat DateFormatterIn1 = new SimpleDateFormat("yyyy-MM-dd");
//private static final SimpleDateFormat DateFormatterIn2 = new SimpleDateFormat("dd/MM/yyyy");
private static final SimpleDateFormat DateFormatterOut = new SimpleDateFormat("dd/MM/yyyy");
private static final SimpleDateFormat DateFormatterIn2 = new SimpleDateFormat("yyyy-MM-dd");
//private static final SimpleDateFormat DateFormatterOut = new SimpleDateFormat("yyyy-MM-dd");
#Autowired
private ResourceBundleMessageSource source;
#Value("${pdf.title}")
private static String pdfTitle;
#Value("${pdf.footerText1}")
private static String pdfFooterText1;
#CrossOrigin(origins = "http://localhost:4200")
#PostMapping("/getPdf")
public ResponseEntity<byte[]> getPDF(
#RequestHeader(name = "Accept-Language", required = true) final Locale locale,
#RequestBody String jsonInput) {
logger.info("myCustomerDetails() method started");
logger.info(jsonInput);
logger.info("locale = {}", locale);
JSONObject data = new JSONObject(jsonInput);
byte[] pdfFile = null;
ResponseEntity<byte[]> response = null;
HttpHeaders headers = new HttpHeaders();
try {
pdfFile = new PdfGenertor().generatePDF(data,locale);
}catch (Exception e) {
e.printStackTrace();
response = new ResponseEntity<>(null, headers, HttpStatus.METHOD_FAILURE);
}
headers.setContentType(MediaType.APPLICATION_PDF);
String filename = "fro_soa_form.pdf";
headers.setContentDispositionFormData(filename, filename);
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
response = new ResponseEntity<>(pdfFile, headers, HttpStatus.OK);
return response;
}
private String formatDate(SimpleDateFormat format, String str) {
try {
Date date = format.parse(str);
return DateFormatterOut.format(date);
} catch (Exception e) {
return "";
}
}
#GetMapping("/sayHello")
public String sayHello() {
return "Hello";
}
}
It the code worked fine in eclipse with postman (PDF displayed with Jason input and header accepted language).
first and second image are all build with "mvn spring-boot:build-image".
If anything I need to post, please let mw know.
Thanks!
Dockerfile:
FROM openjdk:11-slim as build
MAINTAINER xxxx.ca
COPY target/fro_soa_backend-0.0.1-SNAPSHOT.jar fro_soa_backend-0.0.1-SNAPSHOT.jar
ENTRYPOINT ["java","-jar","/fro_soa_backend-0.0.1-SNAPSHOT.jar"]
Well just to tell you that 172.17..0.2 is the ip container so it can't be reached out of the container, it's created from the container to get access from other services or micro-services.
It seems that your app config isn't proprely display to this port, means that your application isn't on 8080 port that's why it gets you empty response, when you added your fonctionalities, you need to use your Dockerfile and expose your application in 8080.
FROM <image_name>
EXPOSE 8080
I cannot figure it out what's wrong. So I just created a new workspace from scratch, import the code, rebuilt the application into Docker with Dockerfile, then the Port mapping get working again.
Now my front Angular and backend Spring boot Apps can communicate on localhost.

Pass Docker arguments to Spring boot properties

In a project locally that I've created to test passing Docker arguments to my spring boot application.properties I have in the application.properties: test.name=${name}
and in the application
#SpringBootApplication
public class RestServiceApplication {
#Value("${test.name}")
private String test;
public static void main(String[] args) {
SpringApplication.run(RestServiceApplication.class, args);
}
#Bean(name = "test")
public String getTest() {
return this.test;
}
}
My Dockerfile:
FROM openjdk:8-jdk-alpine
ARG NAME
ENV TEST_NAME=${NAME}
RUN echo $TEST_NAME
ARG JAR_FILE=target/*.jar
RUN echo JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Building it with sudo docker build -t gs-rest-service --build-arg NAME=Alex . This works fine locally
Now on a different project my application.properties:
bulk.api.username=${BULK_USERNAME}
bulk.api.password=${BULK_PASSWORD}
and my Dockerfile:
FROM maven:3.6.0-jdk-8 AS build
ARG ARTIFACTORY_USER
ARG ARTIFACTORY_PASSWORD
WORKDIR /build
COPY . .
RUN mvn -s settings.xml clean package
ARG BULK_USERNAME
ARG BULK_PASSWORD
ENV BULK_API_USERNAME=${BULK_USERNAME}
ENV BULK_API_PASSWORD=${BULK_PASSWORD}
RUN echo $BULK_API_PASSWORD
FROM openjdk:8-jre-alpine
WORKDIR /opt/cd-graph-import
COPY --from=build /build/target/abc-svc.jar .
ENTRYPOINT ["java", "-jar", "abc-svc.jar"]
My spring boot class:
#SpringBootApplication
public class SpringBootApplication {
#Value("${bulk.api.username}")
private String bulkApiUsername;
#Value("${bulk.api.password}")
private String bulkApiPassword;
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication.class, args);
}
#Bean
public RestTemplate restTemplate() {
return new RestTemplateBuilder().basicAuthentication(bulkApiUsername, bulkApiPassword).build();
}
#Bean(name = "bulkApiUrl")
public String getBulkApiUrl() {
return this.bulkApiUrl;
}
}
When this runs in gitlab with:
docker build -t $SERVICE_IMAGE --build-arg ARTIFACTORY_USER=$ARTIFACTORY_USER --build-arg ARTIFACTORY_PASSWORD=$ARTIFACTORY_PASSWORD --build-arg BULK_USERNAME=$BULKAPI_USERNAME --build-arg BULK_PASSWORD=$BULKAPI_PASSWORD .
I see that $BULK_API_PASSWORD is set properly but when running the application I get the error: `Error creating bean with name
'someApplication': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'BULK_USERNAME' in value "${BULK_USERNAME}"
What am I missing?
According to spring boot application properties docs, you are able to set application properties by an environment variable. You should make env variable with the same name as a property name but if you use env variable, it's recommended to use '_' instead of '.'. For example if you want to set test.name you have to set TEST_NAME env argument.
In the first example, you set TEST_NAME env parameter in your docker file. When your application is starting, spring gets property from env variable (spring gets TEST_NAME, not NAME variable) and passes to the application, which replaces your default value "${name}". Please note here that spring not inject NAME variable, but replace property test.name by value from env variable.
In the second case, you haven't set BULK_API_USERNAME and BULK_API_PASSWORD env properties and spring doesn't replace default values and use ${API_USERNAME} and ${API_PASSWORD} as a properties value. You should set BULK_API_USERNAME and BULK_API_PASSWORD for passing your values to the app.
And also leave the value of properties in application.properties are empty. 'bulk.api.username=' and 'bulk.api.password='

How to override your Propertyfile for jUnit (maven)

I have a Propertyfile config.properties in which I store a Path which a class loads to read Files.
The properties are loaded like this:
public class PropertyConfig {
private static final Properties properties = new Properties();
static {
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties"));
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
}
public static String getSetting(String key) {
return properties.getProperty(key);
}
}
and the call in the relevant class is like this:
private static File savedGamesFolder = new File(PropertyConfig.getSetting("folder_for_saved_games"));
For testing purposes I want to be able to change the path to a test directory, or change the whole Property-file in a jUnit-TestCase. How can achieve this?
I'm using Maven if that helps.
Assuming you have your config.properties in
src/main/resources/config.properties
Note: you should nevertheless have your properties files somewhere in src/main/resources
Place your test configuration in
src/main/test/config.properties
That's it. No need to change your code.

Resources