Access to h2 web console while running junit test in a Spring application - spring

I'm building a Spring application and I need to inspect my H2 in-memory database while I'm running my JUnit tests from a web browser.
In my Spring configuration I have a bean which is responsible of creating my database schema and populating it with some data which will be used within my JUnit tests. I've also added a bean in my test context which creates a web server where I eventually will look for my data.
<bean id="org.h2.tools.Server-WebServer" class="org.h2.tools.Server"
factory-method="createWebServer" init-method="start" lazy-init="false">
<constructor-arg value="-web,-webAllowOthers,-webPort,11111" />
</bean>
Everything seems ok because the database is populated properly since I can access to its data from my JUnit tests and H2 Server only runs while I'm in my test-phase (I can know that, because if I try to access to my_ip:111111 before debugging my tests I cannot connnect but I can connect afterwards once I've started my tests).
Anyway If I open my H2 console from a web browser no schema is shown in it. Any ideas??
Many thanks!!

As this is probably going to be a test-debugging feature, you can add it at runtime with your #Before:
import org.h2.tools.Server;
/* Initialization logic here */
#BeforeAll
public void initTest() throws SQLException {
Server.createWebServer("-web", "-webAllowOthers", "-webPort", "8082")
.start();
}
And then connect to http://localhost:8082/
Note: unless you need this to run as part of your CI build, you'll need to remove this code when you're finished debugging

For future reference here's another way to do it:
Start database and web servers (version can differ):
$ cd .../maven_repository/com/h2database/h2/1.4.194
$ java -cp h2-1.4.194.jar org.h2.tools.Server -tcp -web -browser
TCP server running at tcp://169.254.104.55:9092 (only local connections)
Web Console server running at http://169.254.104.55:8082 (only local connections)
Set database url for tests in code to jdbc:h2:tcp://localhost:9092/mem:mytest.
Run or debug tests.
Click Connect in browser window which opened in step 1.
Jar file for H2 can be downloaded at https://mvnrepository.com/artifact/com.h2database/h2.
Server can be started via #Before in test file like in snovelli's answer, but only in case connection to database in established afterwards, which might be a problem.

I guess the problem is that you are connecting to h2db directly from your application. Not through the server you are launching with bean. Because of this your app and h2db-web-interface can't share one in-memory database.
You should change jdbcUrl in tests to something like jdbc:h2:tcp://localhost/mem:my_DB;DB_CLOSE_DELAY=-1;MODE=Oracle and in browser you should connect to the same url.
With jdbc urls like jdbc:h2:tcp://localhost/... all connections will go through the h2db-server and you can view database state in browser.

If you have defined the jdbc url to something like jdbc:h2:mem:db in your properties, when the database is created it actually gets a bit longer name.
Add a #Autowired DataSource dataSource to your test class, set a debug point somewhere, and inspect that datasource with dataSource.getConnection() and look at the url property. In the case I'm running right this moment, it is
jdbc:h2:mem:43ed83d6-97a1-4515-a925-a8ba53cd322c
Plugging that into the web cosole shows everything I'm expecting.
It isn't the most straightforward way, but it does work.

#snovelli answer above is good.
To debug a particular test case in your IDE, add a infinite loop at the end of the test case and go to browser and launch the console and you can query the data.
Something like below
import org.h2.tools.Server;
/* Initialization logic here */
#BeforeAll
public void initTest() throws SQLException {
Server.createWebServer("-web", "-webAllowOthers", "-webPort", "8082")
.start();
}
#Test
void testMyDBOperation() {
//some db operations like save and get
while(true) {
}
}
now you can go to browser and launch the console at http://localhost:8082/
Of course delete above two changes after debugging

It is not the answer, but a debugging tip.
When you finally access h2-conole http://127.0.0.1:8082/ you may notice that database changes are not shown.
This is because the test cases are not transactional and the data is not committed. Although this behavior is good, as each test case, must run in predefined environment. It is not good if you want to debug and see database changes.
To achieve this, add #Commit annotation above test case and put a dummy line in a #AfterAll annotated method, to stop test and let you see the h2 console ( The h2 server will stop as the test finish).
#AfterAll
public static void finalizeTest() throws Exception {
System.out.print("Just put a break point here");
}
#Test
#Commit
void should_store_an_article() {
// Your test here
}

Related

Custom HealthIndicator not invoked during startup

I implemented a custom HealthIndicator for our application, which is working fine.
I noticed that when I run the application through my IDE (tested with both IntelliJ and Eclipse), the HealthIndicator.health() method is invoked during startup.
However, when I run the application by using the JAR file itself, the HealthIndicator.health() method is not invoked during startup of the application.
Why is the HealthIndicator.health() method not invoked during startup when I run it as a JAR file, and shouldn't it behave similarly as when running it through the IDE?
This is actually not really a bug, but a side effect caused by your IDE. You should be aware that Actuator endpoints are not only exposed over HTTP, but also over JMX. If you take a look at the documentation, you'll also see that the health endpoint is enabled by default on both HTTP and JMX.
Additionally, most IDEs, including IntelliJ and Eclipse, will enable a JMX agent when running the application through the IDE itself. This means that when the application is started, a JMX connection is made, which will in turn trigger the custom health indicator.
You can verify this quite easily, for example let's assume the following health indicator:
#Bean
public HealthIndicator alwaysUpHealthIndicator() {
return () -> {
log.info("Indicator invoked");
return Health.up().withDetail("Foo", "Bar").build();
};
}
If you change your IntelliJ run configuration and disable Enable JMX agent, you'll notice that the message no longer appears in the log.
Likewise, if you disable the health JMX endpoint, you'll also notice that you won't get the additional message within the logs:
management.endpoints.jmx.exposure.exclude=health
This means that you shouldn't rely on the HealthIndicator being executed during the startup of your application. If you have code that should be executed when your application is starting up, consider using an ApplicationRunner or CommandLineRunner bean. For example:
#Bean
public ApplicationRunner applicationRunner() {
return args -> log.info("This will be invoked on startup");
}
I can't answer directly the question, but it looks like there is no real question here, if its a bug - submit a bug to spring boot team. Otherwise its just a statement that I fully agree with.
Either HelathIndicator health() method should be executed in both ways or none at all.
What you're describing sounds more like a weird bug, here is a dirty way to check what happens (remove it or course in production):
Inside the health method of the health indicator obtain a stack trace and print it on console.
Analyze the stack trace and check that its not a result of some /health invocation through the HTTP (for example, maybe your IDE is configured to automatically call actuator's health upon start, who knows)

WebSphere insert/update statements with SQL-SERVER hangs with REQUIRES_NEW propagation

We are facing an issue in our spring batch application when we are deploying the application on WebSphere.
Example: One class contains parent() method and Second class contains child() method, where child method requires a new transaction. After execution of the methods when transaction is committed the commit routine hangs and nothing happens further.
#Transactional //using current transaction
public void parent(){
child();
}
#Transactional(propagation=REQUIRES_NEW) //creates new transaction
public void child(){
//Database save statements including update, insert and deletes
}
This issue only persists in WebSphere and code works fine on our local machine where we are using tomcat as web container.
WebSphere logs/stacktrace shows that the WebSphere prepared statement keeps on waiting for the response from the database. At the same time update and inserts are locked out on the affected tables i.e. if we run an insert or update query manually on the affected table the query doesn't execute.
We are using Spring JPA for data persistence and Spring’s JpaTransactionManager for transaction management and MSSQLServer database.
Is it that WebSphere does not support creating new transaction from existing transaction?
Yes, the pattern you are describing is supported by WebSphere Application Server. Given that this involved locked entries within the database, you might be running into a difference between the application servers in which transaction isolation level is used by default. In WebSphere Application Server, you get a default of java.sql.Connection.TRANSACTION_REPEATABLE_READ for SQL Server, whereas I think in most other cases you end up with a default of java.sql.Connection.TRANSACTION_READ_COMMITTED (less locking). If the default value is the problem, you can change it on the data source configuration.
If you are using WebSphere Application Server Liberty, then the default isolation level can be configured in server.xml as a property of the dataSource element, like this,
<dataSource isolationLevel="TRANSACTION_READ_COMMITTED" jndiName=...
If you are using WebSphere Application Server traditional, then the default isolation level can be configured as the webSphereDefaultIsolationLevel custom property, which can be set to the numeric value of the isolation level constant on java.sql.Connection (value for TRANSACTION_READ_COMMITTED is 2).
See this linked article for the steps of doing so via the admin console.

Multiple Embedded HSQLDB databases in jUnit errors during build

I'm working on a new Spring Batch (3.0.3.RELEASE) application where there will be multiple databases accessed during the jobs. For testing we are using HSQLDB (2.3.2) as the embedded database.
In my Application context I have the following.
<jdbc:embedded-database id="dataSource">
</jdbc:embedded-database>
<jdbc:embedded-database id="proDataSource">
<jdbc:script location="classpath:script-tables.sql" />
<jdbc:script location="classpath:script-constraints.sql" />
</jdbc:embedded-database>
<jdbc:embedded-database id="altDataSource">
<jdbc:script location="classpath:script-alt-tables.sql" />
</jdbc:embedded-database>
When I run a single test in Eclipse, things are fine. When I build from the command line, after the first test, I get errors
Failed to execute SQL script statement at line 3 of resource class path resource [script-promrkt-promo.sql]
object name already exists: PROMRKT
It appears to me that the population process in EmbeddedDatabaseFactory is receiving an already populated database. From what I can tell is that after each test there is not a SHUTDOWN being executed and HSQLDB is leaving the already populated database in memory.
I have re-reviewed the documentation and in a Spring Doc this does show a explicit shutdown command. But if spring starts up the embedded database when my test starts why doesn't it shut it down when the test completes ?
Is it expected the embedded databases will remain after each unit test for the same application context?
What is the order that spring starts up an embedded database and when is the transactional context initialized?
Do I need to use a database cleaner ?
Can the populate be updated to only populate when the database is first started, and rollback to the original script configuration when my test is complete ( kinda like how the AbstractTransactionalSpringContextTests worked )
Do I need some transactional markers? Spring Batch's JobRepo is properly being populated and destroyed between each test. Why are my custom dataSources not ?
The script the log message is complaining about isn't in your configuration. I presume it's being executed somewhere else? If that's the case, you'll probably need to add #DirtiesContext to your tests so that Spring doesn't cache the context (I'm assuming you're using the SpringJunit4Runner with #ContextConfiguration but can't be sure since your actual test isn't in the question).
If my assumption is correct, Spring caches the context in an effort to improve performance over the running of a unit test suite. If your test modifies the context in a way that can impact other tests (like running scripts in one test that need to be run again in others), you mark the tests with #DirtiesContext and Spring won't cache the context. You can use the annotation at either the method or class level. You can read more about the annotation here: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html
I spent a lot of time looking at this and reading the Spring Framework documentation (gasp!) and tracing through code. There are some interesting changes in 4.1 spring core, especially the testing.
I found out that ApplicationContext(s) are cached at the JVM level now. If a second test asks for a context the TestContext looks first in it's cache to see if some other test has already asked for the identical configuration.
I have some profiles for some of my tests. A test with a different profile but the same #ContextConfiguration causes the that context to be re-loaded with the profile applied. When the "Bean Loader" arrives at creating the embedded databases, the EmbeddedDatabaseFactory does not take into consideration that the embedded database (in memory HSQLDB) may have already been created or cached from previous tests and does not need to be re-initialized.
Therefore I added some logic to the EmbeddedDatabaseFactory.initDatabase() checking if the database already exists before re-initializing & running the DatabasePopulator.
List existingDataBases = org.hsqldb.DatabaseManager.getDatabaseURIs();
boolean isExisting = false;
String localDBName = StringUtils.lowerCase(this.databaseName);
for (Object object : existingDataBases) {
if (object.toString().contains(localDBName)) {
isExisting = true;
break;
}
}
// Now populate the database
if (!isExisting && this.databasePopulator != null) {
( of course this isn't quite kosher for what spring would need but it gets the point across )
In my opinion it looks like an issue partially with the EmbeddedDatabaseFactory and the TestContext caching mechanism. My "jdbc:embedded-database" definitions do not have any profiles associated with them. Why does the cache need to re-create them and not load them out of the existing cached beans?
You can try to force creation of new embedded database by setting unique name with generateUniqueName(true) each time new object is created.
Here is an example:
embeddedDatabase = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.generateUniqueName(true)
.addScripts("db/sql/create-db.sql", "db/sql/insert-data.sql")
.build();

release db connections in standalone application

I am working on a standalone application using Spring/JPA and I am trying to release properly the database resources used.
In a Web application using tomcat for example, we shutdown the server, and this way, we let Tomcat manage the resources.
But as I am in a standalone app, I have to take care about this, I use Runtime.getRuntime().addShutdownHook to "catch" the shutdown event and call ((ClassPathXmlApplicationContext) context).close();, something like this:
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
((ClassPathXmlApplicationContext) context).close();
}
It works but with an exception in the stacktrace if a thread was using a connection.
I am wondering if there is another option? Maybe getting a list of open transactions and force them to rollback?
I believe that you would need to implement something like this and inside your destroy method, you would retrieve your datasource and call a close method or something similar. I'm also assuming you have a few things to do when you shutdown your application.
I can't quite help with the right method name as I don't know what you are using for your datasource.

Acceptance testing preloading of data into GAE dev server datastore

In my application I have a set of of DAOs which I inject into my application layer. For an acceptance test I'm writing, I want to preload the dev_server datastore with data, so I use the same Spring config in my JUnit test (using the #ContextConfiguration annotation) to inject an instance of the relevant DAO into my test. When I actually go to store some data eg:
dao.add(entity)
I get the dreaded "No API environment is registered for this thread."
Caused by: java.lang.NullPointerException: No API environment is registered for this thread.
at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppId(DatastoreApiHelper.java:108)
at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppIdNamespace(DatastoreApiHelper.java:118)
....
This is probably because my test case hasn't read in the GAE application-web.xml with the app details (although I'm guessing here I could really be wrong); so it doesn't know to write to the same datastore that the app running on the dev_server is reading/writing to.
How can I get my test to "point" to the same datastore as the app? Is there some "datasource" mechanism that I can inject both into the app and the test? Is there a way to get my test to force the datastore api to read the needed config?
Here is a page that talks about how to do unit tests that connect to a dev datastore. Is this the kind of thing you're looking for? Basically it talks about two classes, LocalServiceTestHelper and LocalDatastoreServiceTestConfig that you can use to set up an environment for testing. While the example given is for unit tests, I believe it will also work for your situation.
You can then configure things like whether the dev datastore is written to disk or just kept in memory (for faster tests). If you want this data to go to the same place as your dev server, you will probably want to adjust this, as I think the default is the "in memory" option. If you look at the javadoc there is a "setBackingStoreLocation" method where you can point to whatever file you want.
I've found the solution!!!!
For some reason the Namespace, AppID and the AuthDomain fields of the test datastore have to match that of the dev_server, then the dev_server can see the entities inserted by the test.
You can see the values for the environment (dev_server or test code) with the following statements
System.out.println(NamespaceManager.get());
System.out.println(ApiProxy.getCurrentEnvironment().getAppId());
System.out.println(ApiProxy.getCurrentEnvironment().getAuthDomain());
In your instance of LocalServiceTestHelper (eg: gaeHelper), you can set the values for the test environment
// the NamespaceManager is thread local.
NamespaceManager.set(NamespaceManager.getGoogleAppsNamespace());
gaeHelper.setEnvAppId(<the name of your app in appengine-web.xml>);
gaeHelper.setEnvAuthDomain("gmail.com");
Then the dev_server will see your entities. However because of synchronisation issues, if the test writes to the datastore after the dev_server has been started the dev_server wont see it unless it can be forced to reread the file (which I haven't figured out yet). Else the server has to be restarted.
I've found a workaround, although it's not very nice because each test method doesn't clean up the Datastore, as explained in the article Local Unit Testing for Java, however, the Datastore starts clean each time the Test class is run, so it's not so bad, provided that you're careful about that.
The problem is, that when using SpringJUnit4ClassRunner, the spring environment is created before the #Before annotation can be run, the solution is use #BeforeClass and use a static variable for LocalServiceTestHelper, to have them created before the Spring Environment is set up.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:META-INF/spring/context-test.xml")
#Transactional
public class MyTest {
#Inject
private MyService myService;
private static final LocalServiceTestHelper helper =
new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig());
#BeforeClass
public static void beforeClass() {
helper.setUp();
}
#AfterClass
public static void afterClass() {
helper.tearDown();
}
If anyone has a better solution, I'll be glad to hear!

Resources