Apache James CLI 는 어떻게 상이한 네트워크 환경의 메일 서버에 접근해서 정보를 획득할까?

seon.woo·2024년 11월 8일
0

Apache James 에서는 아래와 같은 명령어로 서버에 접근하여 데이터를 가져올 수 있다. ( Local 포함 )

$ sudo ./james-cli.sh -h <IP> -p <Port> listusers

그렇다면 저러한 명령어를 실행하는 클라이언트와 클라이언트로부터 전달된 명령어를 수행하는 서버가 있을 것이다.

처음에 예상했던바로는

서버에서 이러한 로그를 출력하고 있기 때문에, Netty 를 통해서 서버-클라이언트 구조를 수립하고 송/수신할 것 이라고 생각했다.

하지만 Netty 내부 어디에도 James-cli.sh 의 명령어인 listUsers, listDomains 와 같은것을 처리하는부분이 없었다.

그리하여 이러한 명령어를 수행하는곳이

james-cli.sh 에서 ServerCmd 란 사실을 찾게 되었고. 해당 클래스에 가면

아래 사진들과 같은 방식으로 명령어를 처리하고 있다.

여기서

case ADDUSER -> probe.addUser(~)

를 보면, probe 객체를 통해 명령어를 처리하고자 하고 있다.
Probe 객체가 뭘까?

    private final JmxDataProbe probe;
    private final JmxMailboxProbe mailboxProbe;
    private final JmxQuotaProbe quotaProbe;
    private final JmxSieveProbe sieveProbe;
    private final JmxDropListProbe dropListProbe;

    public ServerCmd(JmxDataProbe probe, JmxMailboxProbe mailboxProbe, JmxQuotaProbe quotaProbe, JmxSieveProbe sieveProbe,
                     JmxDropListProbe dropListProbe) {
        this.probe = probe;
        this.mailboxProbe = mailboxProbe;
        this.quotaProbe = quotaProbe;
        this.sieveProbe = sieveProbe;
        this.dropListProbe = dropListProbe;
    }
    
ServerCmd.java 일부..

아래의 사진처럼 JMXProbe 를 통해서

작업을 진행하고 있음을 확인할 수 있다.

그래서 JMXProbe 를 통해 어떠한 작업을 하니 살펴보니(아까 addUser)

    public void addUser(String userName, String password) throws Exception {
        try (Closeable closeable = buildMdc("addUser", userName)) {
            usersRepositoryProxy.addUser(userName, password);
        }
    }

아래는 userRepositoryProxy 클래스의 내용물이다.

또 여기서 userRepository(interface)->UsersRepositoryImpl(Class) 에 접근하여 내용을 보면

    @Override
    public void addUser(Username username, String password) throws UsersRepositoryException {
        ensureNoConflict(username);
        assertValid(username);
        usersDAO.addUser(username, password);
    }

어이쿠? userDAO(interface)->public class JPAUsersDAO implements UsersDAO, Configurable 에 접근하고 또 여기서

    @Override
    public void addUser(Username username, String password) throws UsersRepositoryException {
        Username lowerCasedUsername = Username.of(username.asString().toLowerCase(Locale.US));
        if (contains(lowerCasedUsername)) {
            throw new UsersRepositoryException(lowerCasedUsername.asString() + " already exists.");
        }
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        final EntityTransaction transaction = entityManager.getTransaction();
        try {
            transaction.begin();
            JPAUser user = new JPAUser(lowerCasedUsername.asString(), password, algo);
            entityManager.persist(user);
            transaction.commit();
        } catch (PersistenceException e) {
            LOGGER.debug("Failed to save user", e);
            if (transaction.isActive()) {
                transaction.rollback();
            }
            throw new UsersRepositoryException("Failed to add user" + username.asString(), e);
        } finally {
            EntityManagerUtils.safelyClose(entityManager);
        }
    }

이렇게 DB 에 접속하는거 아닌가!?

아니 그렇다면 처음에 말했던 서버-클라이언트 구조에서 클라이언트에서 전송된 데이터가 서버에서 처리하고 이를 반환하는 코드가 별도로 나눠져있을거라고 예상했던 것과 달리 하나의 비즈니스 로직(서버-클라이언트 동일 코드) 에서 처리하고 있음을 알 수 있었다.

그렇다면 어떻게 이게 가능할까?

JMX 덕분에 실제 메일 서버에 접속하여 작업을 처리하기 때문에다.

즉 james-cli.sh 를 통해서 '메일 서버 접속' 을 하고 실제 작업들과 결과들은 클라이언트로 반환함으로써 결과를 전달하는것이 아닌 우리가 원격 서버에 접속해서 작업을 처리하듯이 터미널로 원격 접속을 해서 서버의 명령어를 입력하고, 결과를 직접(터미널의 print) 반환받아 결과를 확인하는것이다.

아 그렇다.

JMX 를 통해서 원격 접속을 하고, 접속한 서버 내에서 데이터 요청을 하기 때문에 모든게 가능했던것이다.

profile
선돌이

0개의 댓글