DEV Community

Masui Masanori
Masui Masanori

Posted on

Run a Micronaut application on Tomcat

Intro

I will try running my Micronaut application on Tomcat in this time.
At first, I tried to access SQL Server using Micronaut R2DBC, as in previous blog posts.

But I couldn't figure out how to use the DataSource what is gotten from JNDI, so I decided to use Doma2.

I install Tomcat ver.10.1.18 on Fedora 39 first.

Generating a war file and running on Tomcat

To generate a war file and run on Tomcat, I change "build.gradle".

build.gradle.kts

plugins {
    id("com.github.johnrengelman.shadow") version "8.1.1"
    id("io.micronaut.application") version "4.2.1"
    id("io.micronaut.aot") version "4.2.1"

    // add this
    id("war")
}
...
graalvmNative.toolchainDetection.set(false)
micronaut {

    // change from "netty"
    runtime("tomcat")

    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("jp.masanori.*")
    }
    aot {
        optimizeServiceLoading.set(false)
        convertYamlToJava.set(false)
        precomputeOperations.set(true)
        cacheEnvironment.set(true)
        optimizeClassLoading.set(true)
        deduceEnvironment.set(true)
        optimizeNetty.set(true)
    }
}
Enter fullscreen mode Exit fullscreen mode

After that, I can generate a war file by "./gradlew war".
The generated war file is put into "{ProjectFolder}/build/libs".

To install into Tomcat, I move the war file into "/opt/tomcat/webapps".

Now, I can access the application on "http://localhost:8080/domasample".
(The file what I put is named "domasample.war")

Adding and getting JNDI

To get connection strings from JNDI, I add some strings into "context.xml" and "server.xml" of Tomcat.

context.xml

...
<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
    <ResourceLink name="jdbc/mssql"
                global="jdbc/mssql"
                auth="Container"
                type="javax.sql.DataSource" />
</Context>
Enter fullscreen mode Exit fullscreen mode

server.xml

...
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
    <Resource name="jdbc/mssql" auth="Container"
            type="javax.sql.DataSource"
        driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
        url="jdbc:sqlserver://localhost:1433;encrypt=false;DatabaseName=master"
        username="sa"
        password="example" />
  </GlobalNamingResources>
...
Enter fullscreen mode Exit fullscreen mode

And I can get JNDI data as DataSource like below.

import javax.sql.DataSource;
import javax.naming.InitialContext;
import javax.naming.NamingException;
...
    try {
      DataSource dataSource = (DataSource)(new InitialContext()).lookup("java:/comp/env/jdbc/mssql");

    }catch(NamingException e) {
    }
...
Enter fullscreen mode Exit fullscreen mode

The problem was I couldn't figure out how to use the DataSource into R2DBC.
"application.yml(application.properties)" has "datasources.*.jndi-name".
But after I set the JNDI name, I couldn't connect.

Using Doma2

In this time, I have only confirmed that Doma2 can connect to SQL Server.
I'll look into it a little more closely next time.

build.gradle.kts

plugins {
    id("com.github.johnrengelman.shadow") version "8.1.1"
    id("io.micronaut.application") version "4.2.1"
    id("io.micronaut.aot") version "4.2.1"
    id("war")
}

version = "0.1"
group = "jp.masanori"

repositories {
    mavenCentral()
}
dependencies {
    annotationProcessor("org.projectlombok:lombok")
    annotationProcessor("io.micronaut:micronaut-http-validation")
    annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
    annotationProcessor("org.seasar.doma:doma-processor:2.55.2")
    implementation("io.micronaut.serde:micronaut-serde-jackson")
    implementation("org.seasar.doma:doma-core:2.55.2")
    compileOnly("io.micronaut:micronaut-http-client")
    compileOnly("org.projectlombok:lombok")
    runtimeOnly("ch.qos.logback:logback-classic")
    runtimeOnly("com.microsoft.sqlserver:mssql-jdbc")
    testImplementation("io.micronaut:micronaut-http-client")
}
application {
    mainClass.set("jp.masanori.Application")
}
java {
    sourceCompatibility = JavaVersion.toVersion("17")
    targetCompatibility = JavaVersion.toVersion("17")
}
graalvmNative.toolchainDetection.set(false)
micronaut {
    runtime("tomcat")
    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("jp.masanori.*")
    }
    aot {
        optimizeServiceLoading.set(false)
        convertYamlToJava.set(false)
        precomputeOperations.set(true)
        cacheEnvironment.set(true)
        optimizeClassLoading.set(true)
        deduceEnvironment.set(true)
        optimizeNetty.set(true)
    }
}
Enter fullscreen mode Exit fullscreen mode

User.java

package jp.masanori.users.models;

import java.time.LocalDateTime;

import org.seasar.doma.Column;
import org.seasar.doma.Entity;
import org.seasar.doma.Id;
import org.seasar.doma.Table;
import io.micronaut.serde.annotation.Serdeable;
import lombok.Getter;
import lombok.Setter;

@Serdeable
@Entity
@Table(name = "users")
public class User {
    @Id
    @Getter
    @Setter
    public Long id;
    @Getter
    @Setter
    public String name;
    @Getter
    @Setter
    @Column(name = "last_update_date")
    public LocalDateTime lastUpdateDate;
}
Enter fullscreen mode Exit fullscreen mode

UserDao.java

package jp.masanori.users.dao;

import org.seasar.doma.Dao;
import org.seasar.doma.Select;
import org.seasar.doma.Sql;
import jp.masanori.DaoConfig;
import jp.masanori.users.models.User;

@Dao
@DaoConfig
public interface UserDao {
    @Select
    @Sql("SELECT * FROM users WHERE id = /*userId*/0")
    User findById(Long userId);
}
Enter fullscreen mode Exit fullscreen mode

DaoConfig.java

package jp.masanori;

import org.seasar.doma.AnnotateWith;
import org.seasar.doma.Annotation;
import org.seasar.doma.AnnotationTarget;

import jakarta.inject.Singleton;

@AnnotateWith(annotations = @Annotation(target = AnnotationTarget.CLASS, type = Singleton.class))
public @interface DaoConfig {   
}
Enter fullscreen mode Exit fullscreen mode

DomaConfig.java

package jp.masanori;

import javax.sql.DataSource;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.seasar.doma.jdbc.Config;
import org.seasar.doma.jdbc.dialect.Dialect;
import org.seasar.doma.jdbc.dialect.MssqlDialect;
import org.seasar.doma.jdbc.tx.LocalTransactionDataSource;
import org.seasar.doma.jdbc.tx.LocalTransactionManager;
import org.seasar.doma.jdbc.tx.TransactionManager;

import jakarta.inject.Singleton;

@Singleton
public class DomaConfig implements Config {

  private final LocalTransactionDataSource dataSource;

  private final LocalTransactionManager transactionManager;

  public DomaConfig() {
    LocalTransactionDataSource dataSource;
    try {
      dataSource = new LocalTransactionDataSource((DataSource)(new InitialContext()).lookup("java:/comp/env/jdbc/mssql"));

    }catch(NamingException e) {
      dataSource = new LocalTransactionDataSource("jdbc:sqlserver://localhost:1433;encrypt=false;databaseName=master;", "sa", "example");
    }
    this.dataSource = dataSource;
    this.transactionManager = new LocalTransactionManager(this.dataSource.getLocalTransaction(getJdbcLogger()));
  }
  @Override
  public DataSource getDataSource() {
    return dataSource;
  }
  @Override
  public Dialect getDialect() {
    return new MssqlDialect();
  }
  @Override
  public TransactionManager getTransactionManager() {
    return transactionManager;
  }
}
Enter fullscreen mode Exit fullscreen mode

UserController.java

package jp.masanori.users;

import org.seasar.doma.jdbc.tx.TransactionManager;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import jp.masanori.DomaConfig;
import jp.masanori.users.dao.UserDao;
import jp.masanori.users.models.User;

@Controller("/")
public class UserController {
    private final UserDao users;
    private final DomaConfig domaConf;
    public UserController(UserDao users, DomaConfig domaConf) {
        this.users = users;
        this.domaConf = domaConf;
    }
    @Get("/users")
    public User findUser() {
        TransactionManager tm = domaConf.getTransactionManager();
        return tm.required(() -> {
            return users.findById(1L);
        });       
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)