【Oracle】パスワードの有効期限

VMwareにOracle11gをインストールしているのですが、
久しぶりにsqlplusにログインしたところ以下のエラーが発生しました。

ERROR:
ORA-28002: the password will expire within 7 days

Oracle10gまではパスワードは無期限でしたが、
Oracle11gからはパスワードの有効期限があるようです。
パスワードの期限を無期限に変更し、
パスワードを再設定します。

sqlplusに管理者権限を持つユーザかsysユーザでログインします。

sqlplus sys/管理者パスワード as sysdba

以下のSQLを実行します。

alter profile default limit password_life_time unlimited;
alter user ユーザ名 identified by "パスワード";

パスワードはダブルクォーテーションで括らないとエラーになります。
記号の区別がつかないためと思われます。

【SQLServer】データのエクスポートとインポート

データのエクスポートとインポートをBCPユーティリティを利用して行います。

bcp データベース名.所有者(初期はdbo).テーブル名 in|out|queryout|format ファイル名 オプション
bcp Test_db.dbo.TestTbl out TestTbl.txt /U ユーザ名 /S サーバ名 /P パスワード /c /t , /r \r\n /q >> exec.log
/c /cを指定しない場合、列ごとにプロンプトでタブや改行を指定する必要がある。
/t /cを指定した場合、デフォルトはタブ区切りになる。変更する場合に指定。
/r /cを指定した場合、デフォルトは\nになる。変更する場合に指定。
/q 空白や単一引用符を含むデータベース名、所有者、テーブル名を指定する場合に指定。
/o bcpコマンド単位でログ出力できる。「>> ファイル名」の方が楽。
/f フォーマット個別指定。/cの方が楽。
   以下がフォーマット。
   8.0
   2
   1 SQLCHAR     0 3 "," 1 USERID Japanese_BIN
   2 SQLDATETIME 0 8 "," 2 DATE   ""

   1行目:バージョン番号
   2行目:列数
   3行目:番号、データ型、プレフィックス長、項目長、終端文字、DB内の項目番号、DB内の項目名、行の照合順序(※1)

※1
照合順序の例
 Japanese_BIN
  大文字、小文字、シングルバイト、ダブルバイト、ひらがな、カタカナを全て区別する
 Japanese_CI_AS
  大文字、小文字、シングルバイト、ダブルバイト、ひらがな、カタカナを全て区別しない

複数テーブルに対して作成する場合、
以下のようなSQL文を準備しておくと汎用性が高くなる。

SELECT 'bcp Test_db.dbo.' + name + ' out ' + name + '.txt /U ユーザ名 /S サーバ名 /P パスワード /c /t "," /r \r\n' FROM sysobjects WHERE type = 'U' ORDER BY name;

【Git】Git導入

今更ながらGitを導入。
最新版のポータブル版を利用します。(現時点では2.15)

  • Git入手
    https://github.com/git-for-windows/git/releases/latest
    PortableGit-X.XX.X-64-bit.7z.exe
  • Git導入
    ダウンロードした7-Zip の 自己解凍ウィザードを起動する。
    任意の場所(今回はD:/DownloadFiles/Git)を指定し、インストールします。
    インストール後、警告が出ましたが、無視。
    ダウンロードしたGit-X.XX.X-64-bit.exeとPortableGit-X.XX.X-64-bit.7z.exeは削除。
  • 環境変数の設定
    環境変数のPathに「D:\DownloadFiles\Git\cmd;」を追加します。
  • 日本語設定
    /// 下記は実施しませんでした。 //////////////////////////////////////

    nkf32.exeを入手します。(nkfwin32.exeは32bit版、nkf.exeは64bit版)
    nkf.exeにリネームし「D:\DownloadFiles\Git\bin」に配置します。

    「D:\DownloadFiles\Git\etc\inputrc」を以下のように編集します。

    set input-meta on
    set output-meta on
    set convert-meta off
    set kanji-code utf-8
    

    「D:\DownloadFiles\Git\etc\profile」の末尾に以下を追加します。

    export GIT_PAGER="nkf -s | LESSCHARSET=utf-8 less"
    export GIT_EDITOR="'D:\DownloadFiles\sakura\sakura.exe' -CODE=4"
    

    /// 上記は実施しませんでした。 //////////////////////////////////////

  • gitconfigの設定
    コマンドプロントを立ち上げ、以下を入力します。

    git config --global core.autocrlf false
    git config --global core.excludesfile ~/.gitignore
    git config --global user.name [ユーザー名]
    git config --global user.email [メールアドレス] 
    git config --global credential.helper wincred
    

    上から順に
    ・改行コードをエディタで明示するので変換なし
    ・全体の .gitignore ファイルの指定
    ・GitHubのユーザ名
    ・GitHubのパスワード
    ・HTTPS で クローンする際にクレデンシャル情報を記憶

    設定値は「C:/Users/XXXX/.gitconfig」に保存されます。

  • gitignoreの設定
    全体設定を「.gitignore」ファイルに行います。
    コマンドプロントに以下を入力します。

    type nul > %USERPROFILE%\.gitignore
    

    Linuxでいうところのtouchコマンドの代わりで0KBのファイルが出来ます。
    「.gitigonore」ファイルにはGitで管理しないファイルを追記します。
    以下を設定しました。(改行コードはLFにしました。)

    # Eclipse
    .metadata
    bin/
    tmp/
    *.tmp
    *.bak
    *.swp
    *~.nib
    local.properties
    .settings/
    .loadpath
    .recommenders
    # External tool builders
    .externalToolBuilders/
    # Locally stored "Eclipse launch configurations"
    *.launch
    # PyDev specific (Python IDE for Eclipse)
    *.pydevproject
    # CDT-specific (C/C++ Development Tooling)
    .cproject
    # Java annotation processor (APT)
    .factorypath
    # PDT-specific (PHP Development Tools)
    .buildpath
    # sbteclipse plugin
    .target
    # Tern plugin
    .tern-project
    # TeXlipse plugin
    .texlipse
    # STS (Spring Tool Suite)
    .springBeans
    # Code Recommenders
    .recommenders/
    # Scala IDE specific (Scala & Java development for Eclipse)
    .cache-main
    .scala_dependencies
    .worksheet
    
  • Eclipseの設定
    EclipseからGitリポジトリを見るため、環境変数に以下を追加します。
    HOME=C:/Users/XXXX

大量の特定ファイルを処理

特定ファイルを処理する場合、
lsやfindした結果をパイプで繋いで処理しますが、
特定ファイルが大量に存在する場合のメモ。

# パターンその1
# ファイルが多すぎるとエラーになる
ls *.txt | xargs rm
# パターンその2
# ファイルが多すぎるとエラーになる
find -f *.txt | xargs rm
# パターンその3
# ファイルが多すぎ手も処理できるがメモリを消費
echo *.txt | xargs rm

【Oracle】これまでに実行したSQL一覧とCPU時間

これまでに実行したSQL一覧とCPU時間を統計データが格納されたビューから取得します。

SELECT SQL_TEXT, FETCHES, EXCUTIONS, LOADS, ELAPSED_TIME, CPU_TIME FROM V$SQL_STATS WHERE SQL_ID = 'xxxxxx';

SQL_TEXT:SQL文
FETCHES:フェッチを行った回数
LOADS:オブジェクトのロード回数
ELAPSED_TIME:SQL の実行に使用した累計の時間
CPU_TIME:SQL の実行に使用した累計の CPU 時間

【Java】マルチスレッド処理

Javaのスレッド処理に関するメモ。
呼び出す側だけを変更すれば対応可能です。
今回の例では1ファイルをタスクに渡しているので、
大量ファイルで1ファイル内のレコード量が少ない時に効果が出ます。
1ファイルで1ファイル内のレコード量が多い場合、タスクに渡す単位を考えた方が良いです。

  • 呼び出す側
    public static void main(String[] args) {
        Z_Thread main = new Z_Thread();
        // スレッド管理サービスを生成する。
        ExecutorService multiExecutor = Executors.newFixedThreadPool(2);
        main.executeMultiTask(multiExecutor);
        // スレッドを終了する。
        multiExecutor.shutdown();
    }
    
    private void executeMultiTask(ExecutorService serviceExecutor) {
        // 戻り値を定義する。
        List<Future<Long>> futures = new ArrayList<Future<Long>>();
        Future<Long> future = null;
        try {
            String fileDir = "D:\\";
            String fileName = "";
            // 指定ディレクトリの「*.txt」の一覧を取得する。
            Path path = Paths.get(fileDir);
            try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path, "*.txt")) {
                // 指定ディレクトリの「*.txt」数分繰り返す。
                for (Path p : directoryStream) {
                    // ファイル名を取得する。
                    fileName = p.getFileName().toString();
                    // 取得したファイル名をタスクに登録する。
                    // タスクはCallableを利用することで戻り値と例外を受け取れるようにする。
                    // タスクはRunnableを利用すると戻り値と例外は受け取れない。
                    Future<Long> future = serviceExecutor.submit(new CallableTest(fileDir, fileName));
                    futures.add(future);
                }
            } catch (IOException e) {
                System.out.println("IO例外");
            }
            // 結果を順次取得する。
            for (Future<Long> futureResult : futures) {
                Long result = futureResult.get(20000, TimeUnit.MILLISECONDS);
                System.out.println("結果:" + result + ":スレッドの結果を受け取りました。");
            }
        } catch (InterruptedException e){
            System.out.println("割り込み例外");
        } catch (ExecutionException e) {
            System.out.println("タスクの中で例外発生");
        } catch (TimeoutException e) {
            System.out.println("タイムアウト例外発生");
        } catch (Exception e) {
            System.out.println("何かの例外発生")
        } finally {
            future.cancel(true);
        }
    }
    

【Java】シングルスレッド処理

Javaのスレッド処理に関するメモ。
JDK1.4ではThreadを利用していたが、JDK1.5以降はExecutorServiceを利用する。

  • 呼び出す側
    public static void main(String[] args) {
        Z_Thread main = new Z_Thread();
        // スレッド管理サービスを生成する。
        ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
        main.executeSingleTask(singleExecutor);
        // スレッドを終了する。
        singleExecutor.shutdown();
    }
    
    private void executeSingleTask(ExecutorService serviceExecutor) {
        // 戻り値を定義する。
        Future<Long> future = null;
        try {
            String fileDir = "D:\\";
            String fileName = "";
            // 指定ディレクトリの「*.txt」の一覧を取得する。
            Path path = Paths.get(fileDir);
            try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path, "*.txt")) {
                // 指定ディレクトリの「*.txt」数分繰り返す。
                for (Path p : directoryStream) {
                    // ファイル名を取得する。
                    fileName = p.getFileName().toString();
                    // 取得したファイル名をタスクに登録する。
                    // タスクはCallableを利用することで戻り値と例外を受け取れるようにする。
                    // タスクはRunnableを利用すると戻り値と例外は受け取れない。
                    future = serviceExecutor.submit(new CallableTest(fileDir, fileName));
                    // Futureクラスのgetメソッドを呼び出すと呼び出し側は結果が返ってくるまで処理を止める。
                    // 20秒を過ぎるとタイム例外が返ってくる。
                    Long result = future.get(20000, TimeUnit.MILLISECONDS);
                    System.out.println("結果:" + result + ":スレッドの結果を受け取りました。");
                }
            }
        } catch (InterruptedException e){
            System.out.println("割り込み例外");
        } catch (ExecutionException e) {
            System.out.println("タスクの中で例外発生");
        } catch (TimeoutException e) {
            System.out.println("タイムアウト例外発生");
        } catch (Exception e) {
            System.out.println("何かの例外発生");
        } finally {
            future.cancel(true);
        }
    }
    
  • 呼び出される側(タスク)(Callableを利用する)
    private String fileDir = "";
    private String fileName = "";
    
    public CallableTest(String fileDir, String fileName) {
        this.fileDir = fileDir;
        this.fileName = fileName;
    }
    
    public Long call() throws Exception {
        try {
            System.out.println(Thread.currentThread().getId() + ":スレッド処理開始");
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("スレッドに割り込みありました。");
                throw new InterruptedException("割り込み例外");
            }
    
            // 何かの処理を想定
            Thread.sleep(10000);
            // コンストラクタで指定したファイルの内容を読み取り表示する。
            List<String> line = Files.readAllLines(Paths.get(fileDir, fileName), Charset.forName("UTF-8"));
            System.out.println(Thread.currentThread().getId() + ":" + fileName + "の内容を表示");
            for (String str : line) {
                System.out.println(str);
            }
        } catch(Exception e){
            System.out.println(Thread.currentThread().getId() + "エラー検知");
            throw e;
        } finally {
            System.out.println(Thread.currentThread().getId() + ":スレッド処理終了");
        }
        return Thread.currentThread().getId();
    }
    

【Java】log4jを利用する

  • log4jの取得
    Javaでlog4jを利用します。
    log4jも1.xと2.xでバージョンがありますが
    2015年8月で1.xの開発は終わっており、
    今は2.xの利用が推奨されています。

    log4j2.xの最新版は以下のサイトの(binary (zip))から入手します。
    http://logging.apache.org/log4j/2.x/download.html

    zipファイルを解凍後、以下のjarをクラスパスに通します。
    log4j-api-2.x.jarとlog4j-core-2.x.jar

  • log4jの設定ファイル
    log4j1.xではlog4j.propertiesだったようですが、
    log4j2.xではlog4j.xmlを利用します。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE project>  
    <Configuration status="off">
    
    <Properties>
        <Property name="format1">%d{yyyy/MM/dd HH:mm:ss.SSS} [%t] [%p] [%c] %m%n</Property>
        <Property name="logfile">D:/test.log</Property>
        <Property name="logfile-archive">D:/test_%d{yyyy-MM-dd}-%i.log</Property>
        <!-- %dは日時。{}に日時の形式を指定 -->
        <!-- %tはスレッド名 -->
        <!-- %pはログレベル名(TRACE, ERROR) -->
        <!-- %cはロガーを生成したクラスフルパス -->
        <!-- %mはログメッセージ -->
        <!-- %nは改行 -->
        <!-- 出力イメージ
             2017/06/25 14:54:20.068 [pool-1-thread-1] [TRACE] [thread.Log] 10:処理開始
             2017/06/25 14:54:20.070 [pool-1-thread-1] [TRACE] [thread.Log] 10:処理終了
        -->
    </Properties>
    
    <Appenders>
        <!-- コンソール出力設定 -->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout>
                <pattern>${format1}</pattern>
            </PatternLayout>
        </Console>
    
        <!-- ファイル出力設定 -->
        <RollingFile name="logfile001" append="true" fileName="${logfile}" filePattern="${logfile-archive}">
            <PatternLayout>
                <pattern>${format1}</pattern>
            </PatternLayout>
            <Policies>
                <!-- 1KBを超えたらローテーション -->
                <SizeBasedTriggeringPolicy size="1KB"/>
                <!-- 日付でローテーション -->
                <TimeBasedTriggeringPolicy />
            </Policies>
            <!-- ローテーションファイルの圧縮率や削除タイミング -->
            <DefaultRolloverStrategy max="3"/>
        </RollingFile>
    </Appenders>
    
    <Loggers>
        <!-- nameは任意の名前かパッケージ名(hoge.fuga.sample)を指定する -->
        <!-- 任意の名前の場合、JavaでLoggerを生成する際にコンストラクタで指定する -->
        <!-- additivityは上位のロガーにもログを出力するかどうか。デフォルトはtrue -->
        <Logger name="WEB" level="info" additivity="false">
            <AppenderRef ref="logfile001" />
        </Logger>
        <Root level="trace">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
    
    </Configuration>
    
  • log4jの利用

    package thread;
    public class Log {
    
        private Logger logger = null;
        private massage = "";
    
        public Log() {
            logger = LogManager.getLogger();
        }
    
        public void writeInfoLog(String message) {
            // messageIDを引数として、メッセージ内容を取得する方法が良い。
            logger.trace(Thread.currentThread().getId() + ":" + message);
        }
    }
    
    package thread;
    public class CallableTest implements Callable<Integer> {
    
        private Log logger = new Log();
        private Log loggerWeb = new Log("WEB");
    
        public Integer call() throws Exception {
            logger.writeInfoLog("ログ");
            loggerWeb.writeInfoLog("ログ");
        }
    }
    

【Java】Javaで外部プロセスを起動する

Javaで外部プロセス(外部のexeファイル)を起動します。
外部プロセスとはxcopyコマンドやffmpegコマンド等です。

例として、ffmpegでmp4をmp3に変換します。
ffmpegは最新版zipを解凍後のフォルダ(D:/DownloadFiles/freesoft/ffmpeg.exe)に存在し、
パスは通していないものとします。

  • 外部プロセス起動設定

    // 外部プロセスの起動のためのコマンドを設定する。
    // Listに設定するものはスペースを含めないように設定する。
    // 今回の例では「D:/DownloadFiles/freesoft/ffmpeg -i D:/test.mp4 -ab 128 D:/test.mp3」となる。
    String mp4File = "D:/test.mp4";
    String mp3File = "D:/test.mp3";
    List<String> cmd = new ArrayList<String>();
    // 「cmd /C」は必要に応じて設定する。
    // echoのようにコマンドプロントが解釈するコマンドを実行する場合に有効です。
    // cmd.add("cmd"); // コマンドプロンプトを起動する。
    // cmd.add("/C"); // コマンドを実行した後に終了を指定する。
    cmd.add("D:/DownloadFiles/freesoft/ffmpeg");
    cmd.add("-i");
    cmd.add(mp4File);
    cmd.add("-ab");
    cmd.add("128");
    cmd.add(mp3File);
    ProcessBuilder processBuilder = new ProcessBuilder(cmd);
    // 標準エラーを標準出力にマージする。
    processBuilder.redirectErrorStream(true);
    
  • 外部プロセス起動その1
    JDK1.7以上であれば今回の方法を選択すると
    外部プロセスの標準エラーを標準出力にマージする設定を活かしつつ、
    出力先を呼び出し側のJavaプロセスに設定できます。

    // 標準出力(標準エラー含む)の内容の出力先をJavaプロセスと同一にする。
    processBuilder.inheritIO();
    // 外部プロセスを起動する。
    Process process = processBuilder.start();
    // 外部プロセスの終了を待機する。
    // この設定を行わないと、「while ((line = br.readLine()) != null)」がないため、
    // Javaプロセスの方が先に処理が進み、「process.exitValue();」で
    // 「process has not exited」というエラーになります。
    process.waitFor();
    // 外部プロセスの結果を出力する。
    int ret = process.exitValue();
    System.out.println("結果:" + ret);
    
  • 外部プロセス起動その2
    外部プロセスの標準エラーを標準出力にマージする設定を活かしつつ、
    出力先を別ファイルに指定できます。

    // 標準出力(標準エラー含む)の内容の出力先を別ファイルに指定する。
    processBuilder.redirectOutput(new File("D:/process.log"));
    // 外部プロセスの終了を待機する。
    process.waitFor();
    // 外部プロセスの結果を出力する。
    int ret = process.exitValue();
    System.out.println("結果:" + ret);
    
  • 外部プロセス起動(非推奨な方法1)
    この方法の欠点として、InputStreamは1024バイトを超える量が保持できないため、
    ロック状態になる可能性があるようです。
    JavaAPIの仕様にも記載があります。

    // 外部プロセスを起動する。
    Process process = processBuilder.start();
    // 標準出力と標準エラーを受け取る。
    InputStream inputStream = process.getInputStream();
    // 受け取ったInputStreamを出力し、外部プロセスの結果を出力する。
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
    String line = null;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
    int ret = process.exitValue();
    System.out.println("結果:" + ret);
    
  • 外部プロセス起動(非推奨な方法2)
    JDK1.4まではRuntimeで起動させていたようですが、
    JDK1.5からはProcessBuilderが内部的にRuntimeを呼び出しているのと、
    標準エラーの内容を標準出力にマージできるようになったりとしているため、
    以下の呼び出し方法は非推奨なようです。

    String cmd = "D:/DownloadFiles/freesoft/ffmpeg -i " + mp4File + " -ab 128 " + mp3File;
    Process process = Runtime.getRuntime().exec(cmd);
    

【ニコニコ動画】コメント取得

ログインしたセッション、コメントサーバURL、ユーザID、スレッドIDを利用し、
コメントを取得します。

  • HTTP通信開始

    // HTTP通信を開始する。
    URL url = new URL(commentServer);
    HttpURLConnection http = (HttpURLConnection)url.openConnection();
    
  • リクエストヘッダ部設定

    // リクエストヘッダ部を設定する。
    http.setRequestMethod("POST");
    http.setDoOutput(true);
    // XML形式を指定する。
    http.setRequestProperty("Content-Type", "text/xml");
    // セッションと動画用クッキーを設定する。
    http.setRequestProperty("Cookie", "user_session=" + userSession);
    
  • リクエストボディ部設定

    // リクエストボディ部を設定する。
    PrintStream ps = new PrintStream(http.getOutputStream());
    StringBuilder xml = new StringBuilder();
    xml.append("<thread ");
    // スレッドIDを設定する。
    xml.append("thread=\"" + threadId + "\" ");
    // 固定値を設定する。
    xml.append("version=\"20061206\" ");
    // 取得コメント数を設定する。一般会員の場合、最大で1000件となる。
    xml.append("res_form=\"-100\" ");
    // ユーザIDを設定する。
    xml.append("user_id=\"" + userId + "\" ");
    xml.append("/>");
    ps.print(xml.toString());
    ps.close();
    
  • レスポンスボディ部取得

    // レスポンスボディ部を取得する。
    String comment = null;
    BufferedReader br = new BufferedReader(new InputStreamReader(http.getInputStream()));
    while((comment = br.readLine()) != null) {
        // XML形式のため、整形する必要がある。
        System.out.println(comment);
    }
    br.close();
    
  • レスポンスヘッダ部の取得は不要です。