質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.78%

Abstractクラスの実装クラスが複数存在する状態でアプリケーションを実行するとAbstractMethodErrorが起きる

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,062

sushikora

score 17

Javaでアプリケーション開発を行っていますが、実行時にAbstractMethodErrorが起きてしまい困っています。

Exception in thread "main" java.lang.AbstractMethodError: javax.ws.rs.core.UriBuilder.uri(Ljava/lang/String;)Ljavax/ws/rs/core/UriBuilder;
    at javax.ws.rs.core.UriBuilder.fromUri(UriBuilder.java:119)
    at org.glassfish.jersey.client.JerseyWebTarget.<init>(JerseyWebTarget.java:71)
    at org.glassfish.jersey.client.JerseyClient.target(JerseyClient.java:211)
    at org.glassfish.jersey.client.JerseyClient.target(JerseyClient.java:72)

以下、実装方法について順を追って説明します。
コードについては、必要箇所のみ抜粋しておりますのでご留意ください。

以下が、基底となるAbstractクラスです。
参照ライブラリであり、javax.ws.rs.coreパッケージに位置します。

package javax.ws.rs.core;

import java.lang.reflect.Method;
import java.net.URI;
import java.util.Map;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.UriBuilderException;
import javax.ws.rs.ext.RuntimeDelegate;

public abstract class UriBuilder {
   protected static UriBuilder newInstance() {
      return RuntimeDelegate.getInstance().createUriBuilder();
   }

   public static UriBuilder fromUri(String uriTemplate) {
      return newInstance().uri(uriTemplate);
   }
   /** 呼び出したいAbstractメソッド */
   public abstract UriBuilder uri(String arg0);

次に、実装クラスです。
参照ライブラリであり、org.glassfish.jersey.uri.internalパッケージに位置します。

package org.glassfish.jersey.uri.internal;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.AccessController;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.ws.rs.Path;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriBuilderException;
import jersey.repackaged.com.google.common.collect.Maps;
import jersey.repackaged.com.google.common.net.InetAddresses;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap;
import org.glassfish.jersey.uri.UriComponent;
import org.glassfish.jersey.uri.UriTemplate;
import org.glassfish.jersey.uri.UriComponent.Type;
import org.glassfish.jersey.uri.internal.UriParser;

public class JerseyUriBuilder extends UriBuilder {
   private String scheme;
   private String ssp;
   private String authority;
   private String userInfo;
   private String host;
   private String port;
   private final StringBuilder path;
   private MultivaluedMap<String, String> matrixParams;
   private final StringBuilder query;
   private MultivaluedMap<String, String> queryParams;
   private String fragment;

   /** UriBuilder.uri(String str)の実装メソッド */
   public JerseyUriBuilder uri(String uriTemplate) {
      if(uriTemplate == null) {
         throw new IllegalArgumentException(LocalizationMessages.PARAM_NULL("uriTemplate"));

      } else {
         UriParser parser = new UriParser(uriTemplate);
         parser.parse();

         String parsedScheme = parser.getScheme();
         if(parsedScheme != null) {
            this.scheme(parsedScheme);
         } else if(this.ssp != null) {

            this.ssp = null;
            parser = new UriParser(this.scheme + ":" + uriTemplate);
            parser.parse();
         }

         this.schemeSpecificPart(parser);

         String parserFragment = parser.getFragment();
         if(parserFragment != null) {
            this.fragment(parserFragment);
         }
         return this;
      }
   }
}

UriBuilderクラスを継承しているクラスが上記の1つだけだと特に問題なく動作します。

しかし、以下クラス群が存在している状態だとエラーになります。
以下は全て参照ライブラリであり、org.restlet.ext.jaxrsパッケージに位置します。

package org.restlet.ext.jaxrs;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriBuilderException;
import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathException;
import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathOnClassException;
import org.restlet.ext.jaxrs.internal.exceptions.IllegalPathOnMethodException;
import org.restlet.ext.jaxrs.internal.exceptions.MissingAnnotationException;
import org.restlet.ext.jaxrs.internal.todo.NotYetImplementedException;
import org.restlet.ext.jaxrs.internal.util.EncodeOrCheck;
import org.restlet.ext.jaxrs.internal.util.Util;
import org.restlet.routing.Template;
import org.restlet.util.Resolver;

/** UriBuilderの実装クラスだが、uri(String str)は実装されていない */
public abstract class AbstractUriBuilder extends UriBuilder {

   public UriBuilder uri(URI uri) throws IllegalArgumentException {
      if(uri == null) {
         throw new IllegalArgumentException("The URI must not be null");
      } else {
         if(uri.getScheme() != null) {
            this.scheme = uri.getScheme();
         }
         if(uri.getHost() != null) {
            this.host = uri.getHost();
         }
         this.port(uri.getPort());
         if(uri.getRawUserInfo() != null) {
            this.userInfo = uri.getRawUserInfo();
         }
         if(uri.getRawPath() != null) {
            this.replacePath(uri.getRawPath());
         }
         if(uri.getRawQuery() != null) {
            this.query = uri.getRawQuery();
         }
         if(uri.getRawFragment() != null) {
            this.fragment = uri.getRawFragment();
         }
         return this;
      }
   }

上記クラスのサブクラスです。

package org.restlet.ext.jaxrs;

import java.lang.reflect.Method;
import java.net.URI;
import java.util.Map;
import java.util.StringTokenizer;
import javax.ws.rs.core.UriBuilderException;
import org.restlet.Application;
import org.restlet.data.Language;
import org.restlet.data.MediaType;
import org.restlet.data.Metadata;
import org.restlet.ext.jaxrs.AbstractUriBuilder;
import org.restlet.ext.jaxrs.internal.util.Util;
import org.restlet.service.MetadataService;

/* AbstractUriBuilderのサブクラス、こちらにもuri(String string)は実装されていない */
public class ExtendedUriBuilder extends AbstractUriBuilder {

   public ExtendedUriBuilder uri(URI uri) throws IllegalArgumentException {
      super.uri(uri);
      return this;
   }
}

これらのライブラリがビルドパスに含まれていると、AbstractMethodErrorとなります。
恐らく、引数がString型のuriメソッドを呼び出したいのに、その実装メソッドが存在しないExtendedUriBuilderクラスをロードしてしまうことで発生しているような気がします。

解決策をいろいろと模索した上で発生した疑問点としては以下2つです。

①ExtendedUriBuilderクラスの含まれるライブラリのビルドパスの優先順位を下げたにも関わらず、AbstractMethodErrorとなるのはなぜでしょうか?末端のサブクラス(ExtendedUriBuilder)が最優先でロードされてしまっているのでしょうか?

②ExtendedUriBuilderクラスをオーバライド(※2)し、Stringを引数に持つuriメソッドを実装したにも
関わらず、そのメソッドが呼出すらされずにAbstractMethodErrorとなるのはなぜでしょうか?末端のサブクラスが最優先でロードされるのであるとすれば、こちらが呼ばれないのは不自然な気がします。

(※2)

import javax.ws.rs.core.UriBuilder;

import org.glassfish.jersey.uri.internal.JerseyUriBuilder;
import org.restlet.ext.jaxrs.ExtendedUriBuilder;

public class UriBuilder2 extends ExtendedUriBuilder{

    @Override
    public UriBuilder uri(String uri) throws IllegalArgumentException {
        JerseyUriBuilder jub = new JerseyUriBuilder();
        jub.uri(uri);
        return this;
    }
}

コード

回答宜しくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

自己解決しました。

UriBuilderの実装クラスを生成するjavax.ws.rs.ext.RuntimeDelegateの実装クラスが、以下2つで重複していたためでした。

・org.restlet.ext.jaxrs.internal.spi.RuntumeDelegateImpl
・org.glassfish.jersey.internal.RuntimeDelegateImpl

実装クラスが重複する場合、何も指定をしない場合前者の実装クラスがロードされてしまうようでした。
前者のクラスはUriBuilderの実装クラスを直接呼び出しているため、オーバライドが効果を発揮していませんでした。
ですので、以下のファイルをsrc/META-INF/servicesに配備することで、後者の実装クラスを優先して
ロードするようにしました。

ファイル名:javax.ws.rs.ext.RuntimeDelegate

org.glassfish.jersey.server.internal.RuntimeDelegateImpl

結果、正常に動作しました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Javaに関する質問
  • Abstractクラスの実装クラスが複数存在する状態でアプリケーションを実行するとAbstractMethodErrorが起きる