アイドルヲタ開発エンジニアの備忘録

個人的な学習した内容の備忘録です。

シェルスクリプトとcronを使ってJavaプロジェクトを自動実行する

はじめに

今回は、「シェルスクリプト」と「cron」を用いて、

Javaプロジェクトを自動実行する手順について記録していきたいと思います。

メインは「シェルスクリプト」と「cron」の設定ですが、個人的にJavaプロジェクトの扱い方に悩んだ時間が長かったため、コンパイルの部分から手順に含めていきます。

 

開発環境

macOS

・JDK17

・Spring Tool Suite4

PostgreSQL

Eclipseで作成したJavaプロジェクトで問題ありません

 

前提

今回は、「削除扱いになっているユーザーデータ」を「削除済みユーザーテーブル」にデータ移行をするJavaプロジェクトを自動実行するというケースで行います。

プロジェクト自体のコードは割愛しますが、実際に使用するクラスは以下の通りです。

 

 ・DBmanager  → DriverManagerクラスを使用して、データベースへの接続、切断を管理するクラス

 ・DeletedUser → 削除済みユーザー情報を持つEntityクラス

 ・DeletedUserDao → 削除済みユーザーテーブルを操作するDaoクラス( insert処理 )

 ・InsertDeletedUser → 実行を行うMainクラス( ユーザ情報を取得し、削除済みユーザーテーブルへinsert )

 ・UserDao → ユーザーテーブルを操作するDaoクラス

         ( 削除扱いユーザー情報のSELECT処理と移行完了データのユーザーテーブルからの削除)

 

 

手順

①プロジェクト外にコンパイル後のクラスを格納するディレクトリの作成

②作成したプロジェクトのコンパイルを行う(postgeSQLドライバーも格納)

コンパイル後のクラスを格納するディレクトリにmanifest.mfを作成する

④jarファイルを作成する

シェルスクリプトファイルを作成する(実行権限の確認も行う)

⑥cronに自動実行を行う処理を記載する

 

①プロジェクト外にコンパイル後のクラスを格納するディレクトリの作成

保存したいディレクトリに移動してから、コンパイル後のクラスを格納する「deleted_user_compile」ディレクトリを作成する。

ターミナル
 

 % cd / Users / 自分のhome / workspace

 % mkdir deleted_user_compile

 

 

②作成したプロジェクトのコンパイルを行う(postgeSQLドライバーも格納)

コンパイルしたいプロジェクトのクラスが格納されているディレクトリの1つ上のディレクトリに移動し、依存関係の数が少ないクラスからコンパイルをする。

※両方満たさないと「シンボルがありません」のエラーが出てコンパイルできません。

 

ターミナル
 
 % cd ./ test_shell_cron / src
 
 % javac -d .. / .. / deleted_user_compile . / shell_cron / DBManager.java
 % javac -d .. / .. / deleted_user_compile . / shell_cron / DeletedUser.java
 % javac -d .. / .. / deleted_user_compile . / shell_cron / UserDao.java
 % javac -d .. / .. / deleted_user_compile . / shell_cron / DeletedUserDao.java
 % javac -d .. / .. / deleted_user_compile . / shell_cron / InsertDeletedUser.java
 

 

今回の場合、依存関係の数が少ないクラスの順番は、

⒈「DBManagerクラス」

⒉「DeletedUserクラス」(エンティティクラス)

⒊「UserDaoクラス」(Daoクラス)

⒋「DeletedUserDaoクラス」(Daoクラス)

⒌「InsertDeletedUserクラス」(Mainクラス)

となっており、ざっくりな順番だと

DBManagerクラス → エンティティクラス → Daoクラス → Mainクラス

で行うと問題ないかと思います。

 

そして、このコンパイルディレクトリに「PostgreSQLドライバー」のjarファイルを格納することを忘れないようにします。

 

この時点でのコンパイルディレクトリの中身は以下のようになります。

▽deleted_user_compile

 

▽deleted_user_compile / shell_cron

 

コンパイル後のクラスを格納するディレクトリにmanifest.mfを作成する

「deleted_user_comile」ディレクトリに移動した後、

manifest.mfを作成し、その中にメインメソッドのクラスの設定を行います。

 

ターミナル
 
 % cd .. / .. / deleted_user_comiple 
 
 % vi manifest.mf
 
 ---------- ↓ viエディタ ------------------
 Main-Class : shell_cron / InsertDeletedUser
 
 ------------------------------------------
 

ここでの注意点は、

「Main-Class:」の後、必ず空欄「 」を入れることと、

「〜/ InsertDeletedUser」の後改行をすることです。

これがないと上手く実行できないことがあります。

 

④jarファイルを作成する

コンパイルしたクラスのjarファイルを作成します。

 

ターミナル
 
 % jar -cvfm insert_deleted_item.jar manifest.mf .
 

 

ちなみにここで指定しているオプションは以下の通りです。

 -c : jarファイルの新規作成
 -v : コンソールに処理内容を表示
 -f  jarファイル名の指定
 -m : マニフェストファイルの指定

 

シェルスクリプトファイルを作成する(実行権限の確認も行う)

Javaプロジェクト(「test_shell_cron」)に戻り、シェルスクリプトファイルを作成する。

(シェルスクリプトファイル自体は別にJavaプロジェクトに入れなくてもいいかもしれませんが、管理しやすいのでこちらに作成します。)

シェルスクリプトファイルには実行したいファイルの記載と、必要なクラスパスを記載します。

 

ターミナル
 
 % cd .. / test_shell_cron / src
 
 % vi insert_deleted_user.sh
 
 ---------↓ viエディタ---------------
 # ! / bin / bash
 
 java -cp / Users / 自分のhome / workspace / deleted_user_compile / postgresql-42.6.0.jar :/ Users / 自分のhome / workspace / deleted_user_compile / insert_deleted_user.jar shell_cron / InsertDeletedUser
 
 ------------------------------------
 

 

まず、1行目に 「#!/bin/bash」もしくは「#!/bin/sh」を記載する。

bashにしかない機能を使用したいときにこの記載がないと動かない可能性があるため記載しておく。

 

そして、自動実行したいクラスを指定する。

java -cp 依存関係にあるクラスのパス(:繋ぎで記載する) 実行したいクラス」

今回は依存関係にあるのは、「postgreSQLドライバー」とメインクラスと依存関係にあるクラスもまとまっている「insert_deleted_user.jarファイル」のため、

その2つのをパスを記載したあと、メインクラスを記載する。

 

ここで、現在の「insert_deleted_user.sh」ファイルの権限を確認し、

「実行権限」が付与されていない場合は付与しておく。

 

ターミナル
 
 % chmod u+x insert_deleted_user.sh
 

 

所有者(ユーザー)に実行権限(x)を持たせる。

 

⑥cronに自動実行を行う処理を記載する

最後にcronに実行したい日時とファイルを記載します。

 

ターミナル
 
 % sudo crontab -u root -e
 Password:(パスワードを入力)
 
 --------↓ crontabファイル----------
 00 22 1 * * / Users / 自分のhome / workspace / test_shell_cron / src /
 insert_deleted_user.sh
 -------------------------------------
 

 

crontabファイルの中は、

「分 時 日 月 曜日 実行したいこと」

を記載します。

今回の場合は「毎月1日22:00にinsert_deleted_user.shファイルを実行する」ということになります。

 

以上となります。

1つパスが間違っていたり、1つプロジェクト内でエラーが出るともちろん自動実行はされません。

実行されないときは、「Javaプロジェクト内のエラー」なのか

シェルスクリプトファイル内のパスの記載ミス」なのか

「crontabのパスや時間設定にミスがないか」なのかを段階ごとに確認すると発見できました。

特に「Javaプロジェクト内のエラー」はSTSEclipse上の実行ではエラーを吐かなかったのに、

ターミナルでJavaプロジェクトを実行するとエラーを吐くなどが結構あったので、

一度コマンドでの実行でエラーが出ないか確認した方がいいなと思いました。

 

もっとスマートな方法もあるのかもしれませんが、発見したらまた別記事でまとめたいと思います。

Spring Securityでのログイン認証機能

はじめに

この記事は、Spring Securityを使用したログイン認証機能の導入について記載しております。

実際に、システムに実装する際に、参考記事を調べてもバージョン違いやMavenのものが多く、メソッドの理解と導入手順に苦戦をしたので、備忘録として書き留めます。

基本的には公式のリファレンスを参考にしております。

 

追記(2023.07.21):

現在、バージョンが変わりこちらの方法も非推奨になってしまっているので、

最新の記載方法を学習次第、書き直したいと思います。

 

開発環境

・JDK17

・Spring Tool Suite4

・Spring Boot 3.1.1

・Spring Security

PostgreSQL

 

前提

今回は既にWebアプリケーションを作成できていることが前提での手順です。

想定するプロジェクト詳細は、通販ECサイト

それぞれのページとURLについては以下の通りで、コードは割愛します。

・トップページ ( / )

・新規会員登録ページ ( /registerMember )

・ログインページ( /loginMember )

・商品一覧ページ ( /showList )

・商品詳細ページ ( /showDetail )

・ショッピングカートページ ( /shoppingCart )

・注文画面 ( /order )

・注文履歴画面 ( /orderHistory )

今回は、ピンク文字の画面はログイン状態にないと遷移できないという設定で進めていきます。

 

実装手順

build.gradleにSpring Securityを追加する

SecurityConfigクラスを作成する (com.example.commonパッケージに格納)

LoginUserクラスを作成する (com.example.domainパッケージに格納)

 

①<build.gradle>にSpring Securityを追加する (今回はテスト用は追加してません)

 plugins {

  id 'java'

  id 'org.springframework.boot' version '3.1.1'

  id 'io.spring.dependency-management' version '1.1.0'

 }

 

 group = 'com.example'

 version = '0.0.1-SNAPSHOT'

 sourceCompatibility = '17'

 

 repositories {

  mavenCentral()

 }

 

 dependencies {

  implementation 'org.springframework.boot:spring-boot-starter-jdbc'

  implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

  implementation 'org.springframework.boot:spring-boot-starter-validation'

  implementation 'org.springframework.boot:spring-boot-starter-web'

  developmentOnly 'org.springframework.boot:spring-boot-devtools'

  runtimeOnly 'org.postgresql:postgresql'

  testImplementation 'org.springframework.boot:spring-boot-starter-test'

   //以下、SpringSecurity使用のため追加が必要

   implementation 'org.springframework.boot:spring-boot-starter-security'

   implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'

 }

 

 tasks.named('test') {

  useJUnitPlatform()

 }

 

ちなみに元々「Spring Stater Project」でプロジェクトを新規作成する際に

最初から依存関係として設定済みのものは

JDBC API

・Thymeleaf

・Validation

・Spring Web

・Spring Boot DevTools

PostgreSQL Driver

になります。

 

②SecurityConfigクラスを作成する

「SeciurityConfig」クラスは、SpringSecurityに関する設定を行うクラス。

「セキュリティ設定の無効化」「ログイン認証に関する認可や遷移ページなどの設定」「ハッシュ化を行う実装オブジェクトの作成」などを設定します。

package com.example.common;

 

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;

import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import org.springframework.security.crypto.password.PasswordEncoder;

import org.springframework.security.web.SecurityFilterChain;

import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

 

@Configuration 

public class SecurityConfig {

 

 /**

 * 特定のリクエストに対して「セキュリティ設定」を無視する設定など全体にかかわる設定ができる.

 * 具体的には静的リソースに対してセキュリティの設定を無効にする。

 * (無効化にしないとデザイン部分を表示するにも認証が必要になってしまうため)

 */

 @Bean

 WebSecurityCustomizer webSecurityCustomizer() {

  return (web) -> web.ignoring().requestMatchers("/css/**", "/img/**", "/js/**");

 }

  

 /**

 * 個々の認可の設定や「ログイン」「ログアウト」についての設定を行う.

 */

 @Bean

 SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

  // 認可に関する設定

  http.authorizeHttpRequests()

    .requestMatchers("/", "/showList", "/showDetail", "/shoppingCart/addItem",          "/shoppingCart/showCartList""/shoppingCart/deleteItem""/registerMember",         "/registerMember/register", "/login").permitAll()

    .anyRequest().authenticated();

 

  // ログインに関する設定

  http.formLogin().loginPage("/loginMember").loginProcessingUrl("/loginMember/login")

          .failureUrl("/loginMember?error=true")

          .defaultSuccessUrl("/showList",false)

          .usernameParameter("email").passwordParameter("password");

 

  // ログアウトに関する設定

  http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/loginMember/logout"))

    .logoutSuccessUrl("/")

    .deleteCookies("JSESSIONID").invalidateHttpSession(true);

 

  return http.build();

  }

 

  /**

  * アルゴリズムのハッシュ化する実装を行うメソッド.

  *

  * @return bcryptアルゴリズムでハッシュ化する実装オブジェクト

  */

 @Bean

 PasswordEncoder passwordEncoder() {

   return new BCryptPasswordEncoder();

 }

 

}

 

SecurityFilterChain securityFilterChain(HttpSecurity http)メソッドについて、

詳しく個々で何を設定しているかというと、

http.authorizeHttpRequests() : 認可に関する設定

 .requestMatchers() : ログイン認証前でもアクセスしたいパスを記載する

  ※注意点として、画面表示のパスだけでなく、紐づく処理を行うパスも記載する

 .permitAll() : 認証なしでアクセスを許可する

 .anyRequest().authenticated() : それ以外のパスは認証が必要

 

 http.formLogin() : ログインに関する設定

 .loginPage() : ログイン画面に設定したい画面のパスを記載する

 .loginProcessingUrl() : ログインボタンを押した時に遷移させるパス(ログイン処理を行うパス)

 .failureUrl() : ログインに失敗した際に遷移させるパス(エラーページに遷移)

 .defaultSuccessUrl() : 第1引数にはデフォルトでログイン成功時に遷移させたい画面のパスを記載

               第2引数には、true (認証後、常に第1引数のパスに遷移) もしくは

               false (認証されず再度ログイン画面に飛ばされてもログインができたら

               第1引数のパスに遷移する)を記載する

  .usernameParameter() : 認証時に使用するユーザー名のリクエストパラメータ名

              (ログイン時に入力する項目※今回はメールアドレス)

  .passwordParameter() : 認証時に使用するパスワードのリクエストパラメータ名

 

  http.logout() : ログアウトに関する設定

 .logoutRequestMatcher() : ログアウトボタンを押した時に遷移させるパスを記載する

                 (ログアウト処理を行うパス)

 .logoutSuccessUrl() : ログアウト後に遷移させたい画面のパスを記載する

   .deleteCookies() : ログアウト後、Cookieに保存されているセッションIDを削除する

   .invalidateHttpSession() : true (ログアウト後、セッションを無効にする) か

               false (ログアウト後、セッションを無効にしない)かを記載する

 

となります。(個人的な認識なので、間違っているところがあったらすみません。)

 

また、今回クラスに@EnableWebSecurityを記載していないのですが、

こちらのプロジェクトはSpringBootを使用しており、

@SpringBootApplicationが実行用クラスに存在しているため省略しています。

(@SpringBootApplication に @EnableWebSecurity が含まれているため)

 

③LoginUserクラスを作成する

DBに格納する会員情報とは別に、SpringSecurityでのログイン認証で使用する時には

権限が必要なため会員情報に権限を付与しています。

package com.example.domain;

 

import java.util.Collection;

 

import org.springframework.security.core.userdetails.User;

import org.springframework.security.core.GrantedAuthority;

 

public class LoginUser extends User{

 

  /** 会員情報(ログインに使用する) */

  private final Member member;

 

  /**

  * 通常の会員情報に加え、認可用ロールを設定する.

  *

  * @param member 会員情報

  * @param authorityList 権限情報が入ったリスト

  */

  public LoginUser(Member member, Collection<GrantedAuthority> authorityList) {

    super(member.getEmail(), member.getPassword(), authorityList);

    this.member = member;

  }

 

  /**

  * 会員情報を返す.

  *

  * @return 会員情報

  */

  public Member getMember() {

    return member;

  }

}

 

以上となります。今回、私が実装したのは通販サイトのログイン機能にSpring Securityのログイン認証を用いるケースだったので、シンプルにログイン機能だけの搭載の学習となると足りないコードなどが出てくるかもしれません。

(知らぬ間に依存関係に助けられているものもあるかもしれないので...)

 

これからもこのように戸惑ったものに関しては、ブログに書き留めていこうと思います。

日々、学習頑張ります。