Issue
I am someone that normally figures things out by myself researching and trying stuff (That's why I had zero questions here). However, I already wasted days trying to figure this out and I am out of options. I hope you guys can help me. I am trying to make my Java/Maven/Hibernate/JPA/Tomcat web application work on a free Heroku account. I was able to deploy successfully and send some data to the Heroku Postgres DB. The same application, running on a local server (Tomcat/IntelliJ), can successfully connect and retrieve data from the Heroku database. However, the deployed Heroku application is not working with the Heroku database. The application is running fine, but I am getting errors when it tries to retrieve data from the database.
I searched here for solutions related to this error and the others in the stack below, including the one already suggested: The meaning of NoInitialContextException error. None seems to fit my situation. The link above mentions using a Properties object, but as I mentioned, it is working fine locally and I never had to use such an object before. If you believe this could be a solution, could you provide more details on how to implement that in my environment?
I don't have a Procfile setup and I am deploying the application using the following Heroku command:
heroku war:deploy target/appname.war --app appname
This is the error stack I am getting (from Heroku log with debug log enabled):
2021-07-18T19:27:42.218347+00:00 app[web.1]: SEVERE: Servlet.service() for servlet [AdminServlet] in context with path [] threw exception [Servlet execution threw an exception] with root cause
2021-07-18T19:27:42.218348+00:00 app[web.1]: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
2021-07-18T19:27:42.218349+00:00 app[web.1]: at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:673)
2021-07-18T19:27:42.218349+00:00 app[web.1]: at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
2021-07-18T19:27:42.218350+00:00 app[web.1]: at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:350)
2021-07-18T19:27:42.218350+00:00 app[web.1]: at javax.naming.InitialContext.getNameParser(InitialContext.java:505)
2021-07-18T19:27:42.218358+00:00 app[web.1]: at org.hibernate.engine.jndi.internal.JndiServiceImpl.parseName(JndiServiceImpl.java:118)
2021-07-18T19:27:42.218358+00:00 app[web.1]: at org.hibernate.engine.jndi.internal.JndiServiceImpl.locate(JndiServiceImpl.java:95)
2021-07-18T19:27:42.218359+00:00 app[web.1]: at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.configure(DatasourceConnectionProviderImpl.java:98)
2021-07-18T19:27:42.218363+00:00 app[web.1]: at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:107)
2021-07-18T19:27:42.218363+00:00 app[web.1]: at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:246)
2021-07-18T19:27:42.218365+00:00 app[web.1]: at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
2021-07-18T19:27:42.218366+00:00 app[web.1]: at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.buildJdbcConnectionAccess(JdbcEnvironmentInitiator.java:145)
2021-07-18T19:27:42.218366+00:00 app[web.1]: at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:66)
2021-07-18T19:27:42.218366+00:00 app[web.1]: at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
2021-07-18T19:27:42.218366+00:00 app[web.1]: at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:101)
2021-07-18T19:27:42.218367+00:00 app[web.1]: at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
2021-07-18T19:27:42.218367+00:00 app[web.1]: at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237)
2021-07-18T19:27:42.218367+00:00 app[web.1]: at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
2021-07-18T19:27:42.218368+00:00 app[web.1]: at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:152)
2021-07-18T19:27:42.218368+00:00 app[web.1]: at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286)
2021-07-18T19:27:42.218369+00:00 app[web.1]: at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243)
2021-07-18T19:27:42.218369+00:00 app[web.1]: at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
2021-07-18T19:27:42.218369+00:00 app[web.1]: at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:176)
2021-07-18T19:27:42.218370+00:00 app[web.1]: at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:127)
2021-07-18T19:27:42.218370+00:00 app[web.1]: at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1224)
2021-07-18T19:27:42.218370+00:00 app[web.1]: at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1255)
2021-07-18T19:27:42.218370+00:00 app[web.1]: at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:56)
2021-07-18T19:27:42.218371+00:00 app[web.1]: at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79)
2021-07-18T19:27:42.218371+00:00 app[web.1]: at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
2021-07-18T19:27:42.218376+00:00 app[web.1]: at dataaccess.HibernateUtil.<clinit>(HibernateUtil.java:12)
2021-07-18T19:27:42.218376+00:00 app[web.1]: at dataaccess.CityDB.getAllCities(CityDB.java:37)
2021-07-18T19:27:42.218376+00:00 app[web.1]: at servlets.AdminServlet.doGet(AdminServlet.java:61)
2021-07-18T19:27:42.218377+00:00 app[web.1]: at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
2021-07-18T19:27:42.218377+00:00 app[web.1]: at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
2021-07-18T19:27:42.218378+00:00 app[web.1]: at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
2021-07-18T19:27:42.218378+00:00 app[web.1]: at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
2021-07-18T19:27:42.218378+00:00 app[web.1]: at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
2021-07-18T19:27:42.218386+00:00 app[web.1]: at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
2021-07-18T19:27:42.218387+00:00 app[web.1]: at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
2021-07-18T19:27:42.218387+00:00 app[web.1]: at filters.AdminFilter.doFilter(AdminFilter.java:35)
2021-07-18T19:27:42.218387+00:00 app[web.1]: at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
2021-07-18T19:27:42.218388+00:00 app[web.1]: at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
2021-07-18T19:27:42.218388+00:00 app[web.1]: at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
2021-07-18T19:27:42.218388+00:00 app[web.1]: at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
2021-07-18T19:27:42.218389+00:00 app[web.1]: at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
2021-07-18T19:27:42.218389+00:00 app[web.1]: at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
2021-07-18T19:27:42.218389+00:00 app[web.1]: at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
2021-07-18T19:27:42.218389+00:00 app[web.1]: at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
2021-07-18T19:27:42.218390+00:00 app[web.1]: at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
2021-07-18T19:27:42.218390+00:00 app[web.1]: at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)
2021-07-18T19:27:42.218391+00:00 app[web.1]: at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
2021-07-18T19:27:42.218391+00:00 app[web.1]: at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
2021-07-18T19:27:42.218391+00:00 app[web.1]: at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1598)
2021-07-18T19:27:42.218391+00:00 app[web.1]: at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
2021-07-18T19:27:42.218392+00:00 app[web.1]: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
2021-07-18T19:27:42.218392+00:00 app[web.1]: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
2021-07-18T19:27:42.218392+00:00 app[web.1]: at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
2021-07-18T19:27:42.218393+00:00 app[web.1]: at java.lang.Thread.run(Thread.java:748)
The error happens when any of the classes in my application calls the EntityManagerFactory method in my HibernateUtil class. This is the code being called:
public class HibernateUtil {
private static final EntityManagerFactory emf =
Persistence.createEntityManagerFactory("AbsPu");
public static EntityManagerFactory getEmFactory() {
return emf;
}
}
This is what my webapp/META-INF/context.xml looks like (I removed sensisitve data). Here I also tried to use the DATABASE_URL system variable from Heroku instead of hard coding the url but I couldn't figure out how to make it work. But it is working like this:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/">
<Resource name="jdbc/postgres" auth="Container"
type="javax.sql.DataSource"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://ec2-52-2-118-38.compute-1.amazonaws.com:5432/...?sslmode=require"
username="..." password="..."
maxActive="100" maxIdle="30" maxWait="10000"
logAbandoned="true" removeAbandoned="true"
removeAbandonedTimeout="60" />
</Context>
And this is what my resources/META-INF/persisence.xml looks like. Again, I just removed the sensitive data (class names):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">
<persistence-unit name="AbsPu">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<non-jta-data-source>java:/comp/env/jdbc/postgres</non-jta-data-source>
<class>...</class>
...
<class>...</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
</persistence-unit>
</persistence>
And lastly, my pom.xml:
<?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.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>...</name>
<URL>...</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.30.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.20</version>
</dependency>
</dependencies>
<build>
<finalName>...</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
I provided as many details as I could think of. Let me know if you need more details!
Solution
Finally got it solved. These are the steps I did to fix the problem:
Enabled JNDI naming by adding "--enable-naming" to the initialization:
heroku config:set WEBAPP_RUNNER_OPTS="--enable-naming" --app appname
. After adding this, I started to get a different error:java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
Based on the new error, I specified the resource factory in my context.xml:
factory="org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory"
And finally, added a new dependency to pom.xml:
<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-dbcp</artifactId> <version>10.1.0-M2</version> </dependency>
The last two steps came from this question: SpringBoot JNDI datasource throws java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory
I believe IntelliJ works fine with JNDI naming by default and has an embedded Database Connection Pool API for Tomcat. That is probably why it was working fine locally and not on Heroku.
Answered By - DiegoWR