Docker: Can't read class path resource from spring boot application - docker

Reading a classpath resource as,
try {
final ClassPathResource classPathResource = new ClassPathResource(format("location%sGeoLite2-City.mmdb", File.separator));
final File database = classPathResource.getFile();
dbReader = new DatabaseReader.Builder(database).build();
} catch (Exception e) {
System.out.println("Exception: " + e);
}
I've packaged this with docker using following Dockerfile,
FROM java:8
ADD build/libs/*.jar App.jar
CMD java -jar App.jar
But while running this application as docker run -p 8080:8080 app-image I can hit the application endpoint and from application logs I can see it fails to read this file (following is from logs),
Exception: java.io.FileNotFoundException: class path resource [location/GeoLite2-City.mmdb] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/App.jar!/BOOT-INF/classes!/location/GeoLite2-City.mmdb
Would appreciate any comment, Things to know before you comment,
**- Running on windows 10, intellij 2018.2, jdk 8
- Can run application successfully with intellij as well as command line
- File exists in jar (I did extract jar and checked )
**

Since you are using springboot you can try to use the following annotation for loading your classpath resource. Worked for me because I had the same exception. Be aware that the directory "location" must be under the src/main/resources folder:
#Value("classpath:/location/GeoLite2-City.mmdb")
private Resource geoLiteCity;
Without springboot you could try:
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("/location/GeoLite2-City.mmdb")) {
... //convert to file and other stuff
}
Also the answers before were correct as the use of "/" is not good at all and File.separator would be the best choice.

It is not a good approach to use slashes.
Always use File Seperators as they work irrespective of System OS.
Change
(location\\GeoLite2-City.mmdb)
to
("location"+ File.separator +"GeoLite2-City.mmdb")
Refer this for more.
https://www.journaldev.com/851/java-file-separator-separatorchar-pathseparator-pathseparatorchar
Difference between File.separator and slash in paths

Had the same issue, the file worked when running the Spring boot app but was not working in Docker. My issue got resolved by using ClassPathResource for the resource and reading the resource as stream using InputStreamReader.
Resource resource = new ClassPathResource("test-xyz.json");
InputStream inputStream = null;
try {
inputStream = resource.getInputStream();
Reader reader = new InputStreamReader(inputStream, "UTF-8");
....

Related

How to get uwsgi to work with internal routing

Hi I'm trying to to get uwsgi to serve static files and untimately to try adding ".js" if the file was not found before defaulting to the python app.
I already compiled the pcre support into my uwsgi but still cannot get it to work.
As a first step I tried to set up a simple example. I created a file settings.ini:
[uwsgi]
route = ^/hi rewrite:/hello.html
route = ^/logo static:/icon.svg
route = ^/huhu static:/hello.html
and run
uwsgi --ini settings.ini --http=127.0.0.1:8000 --check-static static --wsgi-file app.py
The content of app.py is not relevant but I post it for completeness:
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"went to python app"]
If I put files in a subfolder static (e.g. static/hello.html) then they are served correctly, also app.py is called when I navigate to anything that is neither found in the static folder nor matches anything in settings.ini.
However, if I navigate to http://127.0.0.1:8000/hi it goes to the app. I would expect to be served hello.html.
And if I navigate to http://127.0.0.1:8000/huhu I get The connection was reset error on the browser.
I also tried adding plugins = router_static or plugins = router_rewrite to settings.ini with no success.

How to upload file in input file field using Selenium Webdriver when script execution is running on zalenium docker container browser?

I am using Selenium WebDriver for automation. Uploading files in WebDriver is done by simply using the sendKeys() method on the file input field.
Code snippet:
WebElement uploadElement = driver.findElement(By.id("uploadfile"));
// enter the absolute file path into the file input field
uploadElement.sendKeys("C:\\test.txt");
Above code snippet works as expected when script execution is running on Local machine..
But it is not working when script execution is running on Zalenium docker container.
File upload is relatively very simple but slightly different when you are using Docker concept. You need to ensure that you set the file detector for the file (using LocalFileDetector class) which you want to upload.
Refer below code snippet:
WebElement uploadElement = driver.findElement(By.id("uploadfile"));
LocalFileDetector detector = new LocalFileDetector();
File localFile = detector.getLocalFile("C:\\test.txt");
uploadElement.setFileDetector(detector);
// enter the absolute file path into the file input field
uploadElement.sendKeys(localFile.getAbsolutePath());
Above code snippet will upload the file when script execution on Local/Remote/Zalenium docker container.
This worked for me. There is no need to mount volume to get this done
File file = new File(filePath); //my local filepath where the file will be created
File tempDir = new File(System.getProperty("java.io.tmpdir", null), "uploadFile");
if (!tempDir.exists()) {
tempDir.mkdir();
}
File fileToCreate = new File(tempDir, file.getName());
byte[] bytes = Base64.getDecoder().decode(value.getBytes());
FileUtils.writeByteArrayToFile(fileToCreate, bytes);
Thread.sleep(3000);
RemoteWebDriver remoteDriver = new RemoteWebDriver(
new URL("http://localhost:4444/wd/hub"), capabilities);
remoteDriver.setFileDetector(new LocalFileDetector());
remoteDriver.findElement(locator).sendKeys(fileToCreate.toString());

symfony/yaml backed symfony/config not parsing environment variables

I have recreated a simple example in this tiny github repo. I am attempting to use symfony/dependency-injection to configure monolog/monolog to write logs to php://stderr. I am using a yaml file called services.yml to configure dependency injection.
This all works fine if my yml file looks like this:
parameters:
log.file: 'php://stderr'
log.level: 'DEBUG'
services:
stream_handler:
class: \Monolog\Handler\StreamHandler
arguments:
- '%log.file%'
- '%log.level%'
log:
class: \Monolog\Logger
arguments: [ 'default', ['#stream_handler'] ]
However, my goal is to read the path of the log files and the log level from environment variables, $APP_LOG and LOG_LEVEL respectively. According to The symphony documentations on external paramaters the correct way to do that in the services.yml file is like this:
parameters:
log.file: '%env(APP_LOG)%'
log.level: '%env(LOGGING_LEVEL)%'
In my sample app I verified PHP can read these environment variables with the following:
echo "Hello World!\n\n";
echo 'APP_LOG=' . (getenv('APP_LOG') ?? '__NULL__') . "\n";
echo 'LOG_LEVEL=' . (getenv('LOG_LEVEL') ?? '__NULL__') . "\n";
Which writes the following to the browser when I use my original services.yml with hard coded values.:
Hello World!
APP_LOG=php://stderr
LOG_LEVEL=debug
However, if I use the %env(VAR_NAME)% syntax in services.yml, I get the following error:
Fatal error: Uncaught UnexpectedValueException: The stream or file "env_PATH_a61e1e48db268605210ee2286597d6fb" could not be opened: failed to open stream: Permission denied in /var/www/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php:107 Stack trace: #0 /var/www/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php(37): Monolog\Handler\StreamHandler->write(Array) #1 /var/www/vendor/monolog/monolog/src/Monolog/Logger.php(337): Monolog\Handler\AbstractProcessingHandler->handle(Array) #2 /var/www/vendor/monolog/monolog/src/Monolog/Logger.php(532): Monolog\Logger->addRecord(100, 'Initialized dep...', Array) #3 /var/www/html/index.php(17): Monolog\Logger->debug('Initialized dep...') #4 {main} thrown in /var/www/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php on line 107
What am I doing wrong?
Ok you need a few things here. First of all you need version 3.3 of Symfony, which is still in beta. 3.2 was the released version when I encountered this. Second you need to "compile" the environment variables.
Edit your composer.json with the following values and run composer update. You might need to update other dependencies. You can substitute ^3.3 with dev-master.
"symfony/config": "^3.3",
"symfony/console": "^3.3",
"symfony/dependency-injection": "^3.3",
"symfony/yaml": "^3.3",
You will likely have to do this for symfony/__WHATEVER__ if you have other symfony components.
Now in you're code after you load your yaml configuration into your dependency container you compile it.
So after you're lines here (perhaps in bin/console):
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . DIRECTORY_SEPARATOR . '..'));
$loader->load('services.yml');
Do this:
$container->compile(true);
Your IDE's intellisense might tell you compile takes no parameters. That's ok. That's because compile() grabs its args indirectly via func_get_arg().
public function compile(/*$resolveEnvPlaceholders = false*/)
{
if (1 <= func_num_args()) {
$resolveEnvPlaceholders = func_get_arg(0);
} else {
. . .
}
References
Github issue where this was discussed
Pull request to add compile(true)
Using this command after loading your services.yaml file should help.
$containerBuilder->compile(true);
given your files gets also validated by the checks for proper configurations which this method also does. The parameter is $resolveEnvPlaceholders which makes environmental variables accessible to the yaml services configuration.

Grails 1.3.5: How to configure Datasource.groovy to either connect to MySQL or SQL Server

The grails application I am developing could run against MySQL or SQL Server. I want to add a new property in application.properties file say
database.type=MySQL // or it could be SQLSERVER
How do I get this property in Datasource.groovy so that if it is MySQL I connect to MySQL server or it is SQLSERVER I connect to SQL Server?
Is this the correct way to do it? How can I do it?
EDIT: After reading and searching for options I figured the following way explained.
I have created config.properties file in /grails-app/conf/ folder.
driverClassName = com.microsoft.sqlserver.jdbc.SQLServerDriver
dataSource.url = jdbc:sqlserver://localhost:1433;databaseName=testDB
dataSource.username = sa
dataSource.password = sa
Also updated Config.groovy
grails.config.locations = ["classpath:config.properties"]
But I get the below error
Unable to load specified config location classpath:config.properties : class path resource [config.properties] cannot be opened because it does not exist
But if use
grails.config.locations = ["file:E:/workspace/SpringSource2.3.3/GRAILS_PRO/config.properties"]
The application starts up and is able to connect to the database. I don't want to use static file path. What is wrong when using classpath?
Have the same issue for both 'run-app' and 'war' mode i.e. same file does not exist error.
2nd EDIT:
After so much frustration of using classpath and not able to get it to work, I resorted to using environment property. Since server will have CATALINA_HOME defined, I used the below to build the path for external configuration file.
def CATALINA_HOME = "CATALINA_HOME"
def CONFIG_FILE_NAME = "db_config.properties"
if(!grails.config.locations || !(grails.config.locations instanceof List)) {
grails.config.locations = []
}
if(System.getenv(CATALINA_HOME)) {
def fullPath = System.getenv(CATALINA_HOME) + File.separator + "webapps" + File.separator + "${appName}" + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + CONFIG_FILE_NAME
grails.config.locations << "file:" + fullPath
} else {
println "Missing configuration!"
}
The above solution is Tomcat specific. I really would like to see classpath version working!
Thank You.
Regards,
Jay Chandran.
put it in the home directory of the user which is running the instance of tomcat or there should be a .grails directory created for your app , put it under there

Struts2 example, not inserting records in mysql database because connection object is getting null

This Code is working fine with simple application so the drivers are fine.
so why the connection object is not able to initialise with drivers.
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import com.opensymphony.xwork2.ActionSupport;
public class Insert extends ActionSupport {
public String execute() throws Exception, SQLException {
String sql = "";
String url = "jdbc:mysql://localhost:3306/test";
//String dbName = "test";
String driverName = "org.gjt.mm.mysql.Driver";
String userName = "root";
String password = "root";
Connection con=null;
Statement stmt=null;
try {
Class.forName(driverName).newInstance();
con = DriverManager.getConnection(url, userName, password);
stmt = con.createStatement();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
What does the exception say? A NullPointerException on createStatement?
In any case, has the class been loaded; is the class on the classpath (i.e. the MySQL jar on the classpath?)
Check the object returned from Class.forName() to check whether the class has been found.
After your comment it is clear that you have a classpath problem. The mysql jar is not on the classpath. I assume you are talking about a web app deliverable (war file), as changing the build path in eclipse is trivial.
In a web application deployed in, for example, tomcat you can look into <webapp-name>/WEB-INF/lib. The file WEB-INF/lib/mysql-connector-java-5.0.5.jar, or something similar, should be there.
If you have a war file (not yet deployed) you can extract it using the command line tool "jar", or get a file listing from it. If you do (on a command line) jar tf | grep mysql the jarfile should be visible. If you use windows; WinRAR (and probably WinZip) can also open warfiles. In WEB-INF/lib a MySQL jar should be visible.
If you use maven to build your web app; don't forget to add a dependency to the mysql jar. If you use ant to build; don't forget to copy the mysql jar file into WEB-INF/lib before creating the war file.
Please note that currently the recommended driver to ask for is 'com.mysql.driver.Driver', and not 'org.gjt.mm.mysql.Driver'. You can try to load that Driver class in stead of the older driver; further; check whether this driver is actually in the mysql jar (jar tf mysq.jar | grep Driver or so). If the Driver is in the mysql jar and the mysql jar is on the classpath of the webapp (in WEB-INF/lib) and there is only one mysql jar there (version conflicts are no fun), and it still does not work I really don't know what could be wrong. I think I'd download the driver jar again from MySQL and try again.

Resources