DEV Community

Masui Masanori
Masui Masanori

Posted on

1

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

Image of Docusign

Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more