Java Webアプリケーションにおけるサーバー統合とログ管理

1. サーバー統合の実装

1.1 プロジェクト準備

既存の学生情報管理システムをブラウザからアクセス可能なWebアプリケーションに改造します。

  • 既存コードを現在のプロジェクトにコピー
  • パッケージパスの不整合を修正
  • HTTPリクエスト解析と動的リソース処理機能を実装

1.2 HTTPリクエスト処理クラス

URLパラメータを解析し、Map形式で管理するHttpRequestクラスを実装します。

public class HttpRequest {
    private Map<String,String> queryParams = new HashMap<>();

    public void parse() {
        try {
            SocketChannel channel = (SocketChannel) selectionKey.channel();
            StringBuilder buffer = new StringBuilder();
            ByteBuffer data = ByteBuffer.allocate(1024);
            int length;
            
            while((length = channel.read(data)) > 0) {
                data.flip();
                buffer.append(new String(data.array(), 0, length));
                data.clear();
            }
            
            parseHttpRequest(buffer);
            extractQueryParams();
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void extractQueryParams() {
        String uri = this.requestURI;
        String[] parts = uri.split("\\?");
        
        if(parts.length == 2) {
            String[] params = parts[1].split("&");
            
            for (String param : params) {
                String[] keyValue = param.split("=");
                if(keyValue.length == 2) {
                    queryParams.put(keyValue[0], keyValue[1]);
                }
            }
        }
    }

    public String getParameter(String key) {
        return queryParams.get(key);
    }
}

1.3 動的リソースプロセッサ

リクエストURIからクエリパラメータを除去し、適切なServletを呼び出す処理を実装します。

public class DynamicResourceProcessor {
    public void handle(HttpRequest request, HttpResponse response) {
        String uri = request.getRequestURI();
        String basePath = uri.split("\\?")[0];
        
        HttpServlet servlet = ServletConcurrentHashMap.map.get(basePath);
        if(servlet != null) {
            servlet.service(request, response);
        }
    }
}

1.4 学生管理Servlet

学生情報のCRUD操作を行うServletを実装します。

@WebServlet(urlPatterns = "/api/student")
public class StudentServlet implements HttpServlet {
    private StudentService service = new StudentService();

    @Override
    public void service(HttpRequest request, HttpResponse response) {
        String action = request.getParameter("action");
        
        switch(action) {
            case "create":
                createStudent(request, response);
                break;
            case "delete":
                deleteStudent(request, response);
                break;
            case "update":
                updateStudent(request, response);
                break;
            case "list":
                listStudents(request, response);
                break;
        }
    }

    private void listStudents(HttpRequest request, HttpResponse response) {
        Student[] students = service.getAllStudents();
        StringBuilder output = new StringBuilder();
        
        for (Student student : students) {
            output.append(student.getId())
                  .append(", ")
                  .append(student.getName())
                  .append(", ")
                  .append(student.getAge())
                  .append(", ")
                  .append(student.getBirthday())
                  .append("<br>");
        }
        
        response.setContentType("text/html;charset=UTF-8");
        response.write(output.length() > 0 ? output.toString() : "学生データがありません");
    }

    private void createStudent(HttpRequest request, HttpResponse response) {
        String id = request.getParameter("id");
        
        if(service.studentExists(id)) {
            response.setContentType("text/html;charset=UTF-8");
            response.write("IDが重複しています");
            return;
        }
        
        try {
            Student student = new Student();
            student.setId(id);
            student.setName(request.getParameter("name"));
            student.setAge(Integer.parseInt(request.getParameter("age")));
            student.setBirthday(request.getParameter("birthday"));
            
            service.registerStudent(student);
            response.setContentType("text/html;charset=UTF-8");
            response.write("学生登録が完了しました");
            
        } catch (Exception e) {
            response.write("入力データに誤りがあります");
        }
    }

    private void updateStudent(HttpRequest request, HttpResponse response) {}
    private void deleteStudent(HttpRequest request, HttpResponse response) {}
}

2. 単体テスト

2.1 JUnitの基本利用

JUnitはJavaの主要な単体テストフレームワークです。

  • オープンソースで利用可能
  • アノテーションベースのテスト記述
  • 開発効率と品質向上に貢献

2.2 テスト実装手順

  1. junit-4.9.jarをプロジェクトに追加
  2. publicで引数なしのvoidメソッドを作成
  3. @Testアノテーションでテストメソッドを識別
  4. JUnitランナーでテスト実行
public class CalculatorTest {
    @Test
    public void testAddition() {
        int result = 10 + 20;
        System.out.println(result);
    }
}

2.3 ライフサイクルアノテーション

アノテーション 役割
@Test テストメソッドの識別
@Before 各テスト前に実行
@After 各テスト後に実行
public class LifecycleTest {
    @Before
    public void setup() {
        System.out.println("初期化処理");
    }
    
    @Test
    public void testCase() {
        System.out.println("テスト実行");
    }
    
    @After
    public void cleanup() {
        System.out.println("後処理");
    }
}

3. ログ管理

3.1 ログの重要性

アプリケーションの動作履歴を永続的に記録する仕組みです。

比較項目 標準出力 ログ技術
無効化 コード変更が必要 設定変更のみで可能
出力先 コンソールのみ ファイル・DBへの保存
パフォーマンス メインスレッドに影響 別スレッドでの記録

3.2 Log4jの基本構成

ApacheのOSSであるLog4jは柔軟なログ設定が可能です。

  • 出力先の自由な指定(コンソール、ファイルなど)
  • ログレベルによる出力制御
  • 設定ファイルによる運用管理

3.3 基本実装

  1. Log4j関連ライブラリを導入
  2. log4j.properties設定ファイルを作成
  3. Loggerインスタンスを取得
  4. 必要なレベルでログ出力
# log4j.properties
log4j.rootLogger=debug,console,file

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d %t %5p %c{1}:%L - %m%n

log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=/var/logs/app.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %5p %c{1}:%L - %m%n
public class LoggingExample {
    private static final Logger logger = LoggerFactory.getLogger(LoggingExample.class);

    public static void main(String[] args) {
        logger.debug("デバッグ情報");
        logger.info("通常情報");
        logger.warn("警告情報");
        logger.error("エラー情報");
    }
}

3.4 主要設定要素

3つのコアコンポーネント

  • Loggers: ログレベル(DEBUG < INFO < WARN < ERROR < FATAL)
  • Appenders: 出力先(org.apache.log4j.ConsoleAppender, FileAppender)
  • Layouts: 出力形式(PatternLayoutが最も汎用的)

基本設定書式

log4j.rootLogger=ログレベル,出力先1,出力先2,...

3.5 アプリケーションへの適用

@WebServlet(urlPatterns = "/auth/login")
public class AuthenticationServlet implements HttpServlet {
    private static final Logger logger = LoggerFactory.getLogger(AuthenticationServlet.class);

    @Override
    public void service(HttpRequest request, HttpResponse response) {
        logger.info("認証リクエストを受信");
        
        response.setContentType("text/html;charset=UTF-8");
        response.write("認証成功");
        
        logger.debug("認証レスポンスを送信完了");
    }
}

タグ: Java Servlet JUnit Log4j Web Application

5月14日 11:59 投稿