Java SwingでMP3を再生しようとしたらエラーが出た・・・

java audioエラー

ソースは、下のようなものです。

    public AudioUtil(String path) throws RpgException {
        try {
            InputStream in = Files.newInputStream(Paths.get(path));
            audioStream = AudioSystem.getAudioInputStream(in);
            AudioFormat audioFormat = audioStream.getFormat();
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
            audioClip = (Clip) AudioSystem.getLine(info);
            audioClip.addLineListener(this);
        } catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) {
            throw new RpgException(e.getMessage());
        }
    }

テストクラスは、以下

    public void initAudio() {
        try {
            target = new AudioUtil("game/stories/html/title.mp3");
        } catch (RpgException e) {
            e.printStackTrace();
            fail(e.getMessage());
        }
    }

スタックトレース

1つめのエラー

mark/reset not supported

jp.zenryoku.rpg.exception.RpgException: mark/reset not supported
at jp.zenryoku.rpg.utils.AudioUtil.(AudioUtil.java:24)
at jp.zenryoku.rpg.AudioTest.initAudio(AudioTest.java:18)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

java.lang.AssertionError: mark/reset not supported

at org.junit.Assert.fail(Assert.java:91)
at jp.zenryoku.rpg.AudioTest.initAudio(AudioTest.java:21)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

2つめのエラー

Stream of unsupported format

Java SoundはMP3をサポートしていないようだ。。。
JavaFXのmediaを使用します。

jp.zenryoku.rpg.exception.RpgException: Stream of unsupported format
at jp.zenryoku.rpg.utils.AudioUtil.(AudioUtil.java:25)
at jp.zenryoku.rpg.AudioTest.initAudio(AudioTest.java:18)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

java.lang.AssertionError: Stream of unsupported format

at org.junit.Assert.fail(Assert.java:91)
at jp.zenryoku.rpg.AudioTest.initAudio(AudioTest.java:21)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

JavaFX Media

MP3の再生にはJavaFXのライブラリを使用するみたいなので、そのようにします。Mavenでライブラリを追加。

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-media</artifactId>
            <version>11</version>
            <classifier>linux</classifier>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-graphics</artifactId>
            <version>11</version>
            <classifier>win</classifier>
        </dependency>
    </dependencies>

次のエラー

java.lang.IllegalStateException: Toolkit not initialized

スタックオーバーフローに聞いてみました。
下のように実装するということでしたが、今は別な方法があるようで。。。

public class JavaFXInitializer extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        // JavaFX should be initialized
        someGlobalVar.setInitialized(true);
    }
}
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        new JFXPanel(); // initializes JavaFX environment
        latch.countDown();
    }
});
latch.await();

最終的に解決

JavaFXのJFXPanelを起動してやればOK!
次の依存関係を追加します。

        <!-- JavaFX -->
        <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-swing -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-swing</artifactId>
            <version>11-ea+24</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-media</artifactId>
            <version>11</version>
            <classifier>mac</classifier>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-media</artifactId>
            <version>11</version>
            <classifier>linux</classifier>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-graphics</artifactId>
            <version>11</version>
            <classifier>win</classifier>
        </dependency>

こんな感じでJFXPanelを起動

public class JavaFXInitializer {
    public static void launch() {
        Platform.startup(() ->{
            new JFXPanel();
        });
    }
}

オーディオを再生

public class AudioUtil {
    private MediaPlayer player;
    public AudioUtil(String path) throws RpgException {
        try {
            JavaFXInitializer.launch();
            URL url = Paths.get(path).toFile().toURI().toURL();
            Media media = new Media(url.toExternalForm());
            player = new MediaPlayer(media);
        } catch (MalformedURLException e) {
            throw new RpgException(e.getMessage());
        }

    }

    public void play() throws RpgException {
        player.play();
    }
    public void stop() throws RpgException {
        player.stop();
    }

}

JAXB関連のエラー対応

JAXB関連のエラー

エラーその1

Java Swingでの開発中に表題の様なエラーが出ました。

javax.swing.SingleSelectionModelはインタフェースです。JAXBはインタフェースを処理できません。

実装内容としては、次のようになっています。

  1. XMLファイルを読み込み
  2. 対応するデータをクラスで保持する⇔XMLファイルに出力

このエラーは、

実装部分としては下のようなコードです。
<処理部分>

public class InputSelector extends JPopupMenu {
    ・・・
    private void openCommandMenu(SelectMenu item) {
        List<Command> cmdList = play.getJob().getCommandList();
        for (Command cmd : cmdList) {
            cmd.addActionListener(this);
            add(cmd);
            addSeparator();
        }
    }
    ・・・
} 

add()しているCommandクラスをJMenuItemで拡張しています。※拡張したらエラーになりました。
JMenuItemを継承したのでエラーになりました。
なので、このクラスを保持するクラスを作成して、エラーを直しました。

@Data
public class Command extends JMenuItem implements ConfigIF {
    private String id;
    private String name;
    private Formula formula;

    public Command() {
    }

    public Command(String id, String name, Formula formula) {
        this.id = id;
        this.name = name;
        this.formula = formula;
    }
}

新たに作成したクラス
<CommandMenu>

import javax.swing.*;

public class CommandMenu extends JMenuItem {

    private Command command;

    public CommandMenu() {
    }

    public CommandMenu(Command cmd) {
        super(cmd.getName());
        this.command = cmd;
    }

    public Command getCommand() { return command; }
    public void setCommand(Command cmd) { this.command = cmd; }
}

余談1

JAXBでXMLからクラスを生成するとき、対象のクラスには、デフォルトコンストラクタが定義されていないとエラーになる。
<XMLの一部抜粋>

<config>
    <views>HP</views>
    <views>MP</views>
    <views>LV</views>
    <money>
        <key>NIG</key>
        <name>ニギ</name>
        <value>0</value>
    </money>
    <money>
        <key>GLD</key>
        <name>ゴールド</name>
        <value>1</value>
    </money>
    <element>
        <id>FIR</id>
        <name>火</name>
    </element>
    <element>
        <id>WIN</id>
        <name>風</name>
    </element>
    <element>
        <id>WAT</id>
        <name>水</name>
    </element>
    <element>
        <id>EAT</id>
        <name>土</name>
    </element>
</config>

<クラス>

package jp.zenryoku.rpg.data.config;

import lombok.Data;
/**
 * このゲームで使用する要素・エレメントを定義する。
 * 
 * @author (Takunoji)
 * @version (1.0)
 */
@Data
public class Element
{
    /** ID */
    private String id;
    /** 要素名 */
    private String name;

    /** デフォルトコンストラクタ */
    public Element() {
    }

    public Element(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

エラーその2

下の様なエラーが出ました。

javax.xml.bind.DataBindingException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
プロパティmoneyが存在しますが、@XmlType.propOrderに指定されていません

XML出力の順番を指定する「\@XmlType(propOrder=XXX)」の指定の中にフィールド変数の「money」が登録されていなかった。
下のコードは追加した後です。

対象のクラス

@XmlRootElement( name="player")
@XmlType(propOrder={"level", "name", "sex", "status", "items", "job", "state", "wepon", "armor", "money"})
@Data
public class Player implements Cloneable
{
    private static final boolean isDebug = true;
    /** 名前 */
    protected String name;
    /** レベル */
    protected int level;
    /** 性別 */
    protected SEX sex;
    /** ステータス */
    protected Map<String, Params> status;
    /** 所持アイテム */
    protected List<Item> items;
    /** 職業 */
    protected Job job;
    /** 状態 */
    protected State state;
    /** 武器 */
    protected Wepon wepon;
    /** 防具 */
    protected Armor armor;
    /** お金 */
    protected int money;
    ...
}

エラーその3

javax.xml.bind.DataBindingException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
プロパティmoneyが@XmlType.propOrderにありますが、そのようなプロパティは存在しません。noの誤りである可能性があります。

対象のクラス

もともとholdmneyの部分がmoneyになっていた。
propOrderの中身を正しく書き換えたら治った。

@Data
@XmlRootElement( name="monster")
@XmlType(propOrder={"no", "talk", "message", "type", "holdmoney", "exp"})
public class Monster extends Player implements Cloneable, Serializable
{
    /** 番号 */
    private int no;
    /** 話す */
    private boolean talk;
    /** 話すときのメッセージ */ 
    private String message;
    /** モンスタータイプ */
    private MonsterType type;
    /** お金 */
    private int holdmoney;
    /** 経験値 */
    private int exp;
    ...
}

Exception in thread “AWT-EventQueue-0” java.lang.NullPointerException: Cannot invoke “java.lang.CharSequence.toString()” because “replacement” is null

because "replacement" is null

下のようなコードを実行したらエラーが出ました。

// 基本計算記号の変換
Set<String> fSet = formulas.keySet();
for (String key : fSet) {
    System.out.println("key: " + key);
    if (resFormula.contains(key)) {
        Formula f = formulas.get(key);
        resFormula = resFormula.replace(key, f.getFormulaStr());
    }
}

解決方法

String#replace()の第二引数がNULLになっていた。。。

実装は、Githubにアップしてあります。

DeepLeaning 4 Jを使用したらLombokが使えなくなった。

エラーメッセージ

いずれも、getter, setterが見つからないというエラーが出ました。このクラスでは、「\@Data」を使用してgetter, setterを実装しなくてもいいように実装しています。

java: シンボルを見つけられません
  シンボル:   メソッド getName()
  場所: タイプjp.zenryoku.rpg.charactors.Playerの変数 player
java: シンボルを見つけられません
  シンボル:   メソッド getHP()
  場所: クラス jp.zenryoku.rpg.charactors.Player

Lombokが使えなくなった。

Mavenを使用して、DL4Jを依存関係に追加しました。その後Lombokが使えなくなりました。

調査結果

DL4Jでlombokを参照しているのでそちらが優先される。
DL4JはJava17以上、である必要があるので、Java1.8を使用している自分の環境では、lombokが動かなかったというわけです。

対応

JDKを1.8から17に変更する。

これで、lombok問題は解決できました。

JavaFXは入っていない

JDK17になるとJavaFXが使用できない。。。
なので、下のようなエラーが出ました。
これは、別件なので、使用するJDKを1.8に戻し、DL4Jを使用しない方向に切り替えます。

java: シンボルを見つけられません
  シンボル:   クラス FXML
  場所: クラス jp.zenryoku.procon.ProConRPGLogic
java: シンボルを見つけられません
  シンボル:   クラス FXML
  場所: クラス jp.zenryoku.procon.ProConRPGLogic

でわでわ。。。

Java Servlet エラー ~フォワードできません

フォワードできません

以下のようなエラーが出ました。

java.lang.IllegalStateException: レスポンスをコミットした後でフォワードできません

未入力の場合、リクエストスコープにエラーメッセージをリクエストスコープに保存して画面をフォワードしたときに上記のエラーが出ました。

具体的なコードは以下のようなものです。

/** POSTリクエスト */
/**
 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
 */
protected void doPost(HttpServletRequest request
        , HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding("UTF-8");
    String text = request.getParameter("text");

    // 入力チェック
    if (text == null || "".equals(text)) {
        System.out.println("*** in Error ***");
        // エラーメッセージをリクエストスコープに設定
        request.setAttribute("errorMsg", "つぶやきが入力されていません");
        // フォワード
        forwardMain(request, response);
    }
    ServletContext application = this.getServletContext();
    // アプリケーションスコープ
    List<Mutter> mutterList =
            (List<Mutter>)application.getAttribute("mutterList");
    // セッションスコープ
    HttpSession sess = request.getSession();
    // ユーザークラス
    User user = (User) sess.getAttribute("loginUser");
    // つぶやきリストに追加
    Mutter mutter = new Mutter(user.getName(), text);
    new PostMutterLogic().execute(mutter, mutterList);
    // アプリケーションスコープに保存
    application.setAttribute("mutterList", mutterList);
    // フォワード
    forwardMain(request, response);
}

private void forwardMain(HttpServletRequest request
        , HttpServletResponse response) throws ServletException, IOException{
    // フォワード
    RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");
    dispatcher.forward(request, response);
}

エラーの原因を探る

元々エラー処理、パラメータの入力チェックを行っていなかったところにエラー処理if (text == null || "".equals(text)) {を入れ、フォワードするように実装しました。
原因部分はここになるのですが、調べてみると。下の処理を2回行っているところがありました。

    // フォワード
    RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/jsp/main.jsp");
    dispatcher.forward(request, response);

まとめ

2回フォワードしているのでエラーになったということでした。。。

でわでわ。。。