12

Jerseyのクエリ パラメータに Joda を使用したいのDateTimeですが、これはすぐに使用できる Jersey ではサポートされていません。InjectableProviderを実装することがサポートを追加する適切な方法であると想定していDateTimeます。

InjectableProvider誰かがforの適切な実装を教えてくれますかDateTime? または、推奨する価値のある代替アプローチはありますか? Date(コードから、またはコード内で変換できることは承知しStringていますが、これはより少ない解決策のようです)。

ありがとう。

解決:

@ContextGuiceではなくJAX-RSでインジェクションメカニズム を使用するように、以下のGiliの回答を変更しました。

更新: サービス メソッドのパラメーターに UriInfo が挿入されていない場合、これは正しく機能しない可能性があります。

import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider;
import java.util.List;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;
import org.joda.time.DateTime;

/**
 * Enables DateTime to be used as a QueryParam.
 * <p/>
 * @author Gili Tzabari
 */
@Provider
public class DateTimeInjector extends PerRequestTypeInjectableProvider<QueryParam, DateTime>
{
    private final UriInfo uriInfo;

    /**
     * Creates a new DateTimeInjector.
     * <p/>
     * @param uriInfo an instance of {@link UriInfo}
     */
    public DateTimeInjector( @Context UriInfo uriInfo)
    {
        super(DateTime.class);
        this.uriInfo = uriInfo;
    }

    @Override
    public Injectable<DateTime> getInjectable(final ComponentContext cc, final QueryParam a)
    {
        return new Injectable<DateTime>()
        {
            @Override
            public DateTime getValue()
            {
                final List<String> values = uriInfo.getQueryParameters().get(a.value());

                if( values == null || values.isEmpty())
                    return null;
                if (values.size() > 1)
                {
                    throw new WebApplicationException(Response.status(Status.BAD_REQUEST).
                        entity(a.value() + " may only contain a single value").build());
                }
                return new DateTime(values.get(0));
            }
        };
    }
}
4

4 に答える 4

5

Guice に依存する実装を次に示します。マイナーな変更を加えて別のインジェクターを使用できます。

import com.google.inject.Inject;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider;
import java.util.List;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;
import org.joda.time.DateTime;

/**
 * Enables DateTime to be used as a QueryParam.
 * <p/>
 * @author Gili Tzabari
 */
@Provider
public class DateTimeInjector extends PerRequestTypeInjectableProvider<QueryParam, DateTime>
{
    private final com.google.inject.Provider<UriInfo> uriInfo;

    /**
     * Creates a new DateTimeInjector.
     * <p/>
     * @param uriInfo an instance of {@link UriInfo}
     */
    @Inject
    public DateTimeInjector(com.google.inject.Provider<UriInfo> uriInfo)
    {
        super(DateTime.class);
        this.uriInfo = uriInfo;
    }

    @Override
    public Injectable<DateTime> getInjectable(final ComponentContext cc, final QueryParam a)
    {
        return new Injectable<DateTime>()
        {
            @Override
            public DateTime getValue()
            {
                final List<String> values = uriInfo.get().getQueryParameters().get(a.value());
                if (values.size() > 1)
                {
                    throw new WebApplicationException(Response.status(Status.BAD_REQUEST).
                        entity(a.value() + " may only contain a single value").build());
                }
                if (values.isEmpty())
                    return null;
                return new DateTime(values.get(0));
            }
        };
    }
}

Guice バインディングはありません。@Provider は JAX-RS アノテーションです。Guice は UriInfo を注入できる必要があるだけで、Jersey-Guiceはそのバインディングを提供します。

于 2012-11-21T19:23:54.390 に答える
2

クライアント/サーバー間の Joda DateTime オブジェクトの送信を処理する別のオプションは、アダプターとそれに応じたアノテーションを使用して明示的にマーシャリング/デマーシャリングすることです。原則は、デマーシャリングがコンストラクター呼び出しの Long オブジェクトを使用して新しい DateTime オブジェクトをインスタンス化する間、それを Long オブジェクトとしてマーシャリングすることです。Long オブジェクトは getMillis メソッドを介して取得されます。これを機能させるには、DateTime オブジェクトを持つクラスで使用するアダプターを指定します。

@XmlElement(name="capture_date")
@XmlJavaTypeAdapter(XmlDateTimeAdapter.class)
public DateTime getCaptureDate() { return this.capture_date; }
public void setCaptureDate(DateTime capture_date) { this.capture_date = capture_date; }

次に、アダプターと XML クラスを記述して、Long オブジェクトをカプセル化します。

import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

/**
 * Convert between joda datetime and XML-serialisable millis represented as long
 */
public class XmlDateTimeAdapter  extends XmlAdapter<XmlDateTime, DateTime> {

    @Override
    public XmlDateTime marshal(DateTime v) throws Exception {

        if(v != null)
            return new XmlDateTime(v.getMillis());
        else
            return new XmlDateTime(0); 


    }

    @Override
    public DateTime unmarshal(XmlDateTime v) throws Exception {

        return new DateTime(v.millis, DateTimeZone.UTC);
    }
}


import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * XML-serialisable wrapper for joda datetime values.
 */
@XmlRootElement(name="joda_datetime")
public class XmlDateTime {

    @XmlElement(name="millis") public long millis;

    public XmlDateTime() {};

    public XmlDateTime(long millis) { this.millis = millis; }   

}

すべてが計画どおりに進む場合は、アダプターを使用して DateTime オブジェクトをマーシャリング/デマーシャリングする必要があります。アダプターにブレークポイントを設定して、これを確認してください。

于 2014-12-05T02:10:28.263 に答える
1

ドキュメントを読むと、メソッドに String を返す必要があるように見えます。これを DateTime に変換します。DateTime(long) コンストラクターを使用すると思います。(比較的) わかりやすい例があります。 codehaleで、試してみたい場合はお知らせください。

于 2012-11-20T22:22:52.560 に答える
1

@ Gili、申し訳ありませんが、あなたの投稿に直接コメントするのに必要な評判がありませんが、お願いできますか:

  • 実装に使用する import ステートメントを追加しますか?
  • Guice ですべてをバインドする方法の例を追加しますか?

事前にどうもありがとうございました。

M.


問題:

私はHolySamosaと同じことをすることに興味があり、Guiceも使用していますが、以下の問題に直面しています.

私が追加した場合:

bind(DateTimeInjector.class);

私の中GuiceServletContextListenerで、私は得る:

java.lang.RuntimeException: 
The scope of the component class com.foo.mapping.DateTimeInjector must be a singleton

クラスを追加すると@Singleton、次のようになります。DateTimeInjector

GRAVE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for method public java.util.List com.foo.ThingService.getThingByIdAndDate(java.lang.String,org.joda.time.DateTime) at parameter at index 1
SEVERE: Method, public java.util.List com.foo.ThingService.getThingByIdAndDate(java.lang.String,org.joda.time.DateTime), annotated with GET of resource, class com.foo.ThingService, is not recognized as valid resource method.

アドバイス / 解決策:

  • 使用する注釈に注意してください (私とは異なります)。たとえば、実際には@PathParamの代わりに使用していました@QueryParam
  • UriInfo uriInfoサービスでは、メソッドの署名に含める必要はありません。機能パラメータだけで十分であり、UriInfo存在するかどうかに関係なく機能するはずです。
  • インジェクターを取得できるようにするには、Guice を以下のように構成する必要がありました。

例:

// Configure Jersey with Guice:
Map<String, String> settings = new HashMap<String, String>();
settings.put(PackagesResourceConfig.PROPERTY_PACKAGES, "com.foo.mapping");
serve("/*").with(GuiceContainer.class, settings);

完全な解決策:

import java.util.List;

import javax.ws.rs.PathParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;

import org.joda.time.DateTime;

import com.google.inject.Inject;
import com.foo.utils.DateTimeAdapter;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider;

/**
 * Enables DateTime to be used as a PathParam.
 */
@Provider
public class DateTimeInjector extends PerRequestTypeInjectableProvider<PathParam, DateTime> {
    private final com.google.inject.Provider<UriInfo> uriInfo;

    /**
     * Creates a new DateTimeInjector.
     * 
     * @param uriInfo
     *            an instance of {@link UriInfo}
     */
    @Inject
    public DateTimeInjector(com.google.inject.Provider<UriInfo> uriInfo) {
        super(DateTime.class);
        this.uriInfo = uriInfo;
    }

    @Override
    public Injectable<DateTime> getInjectable(final ComponentContext context, final PathParam annotation) {
        return new Injectable<DateTime>() {
            @Override
            public DateTime getValue() {
                final List<String> values = uriInfo.get().getPathParameters().get(annotation.value());

                if (values == null) {
                    throwInternalServerError(annotation);
                }

                if (values.size() > 1) {
                    throwBadRequestTooManyValues(annotation);
                }

                if (values.isEmpty()) {
                    throwBadRequestMissingValue(annotation);
                }

                return parseDate(annotation, values);
            }

            private void throwInternalServerError(final PathParam annotation) {
                String errorMessage = String.format("Failed to extract parameter [%s] using [%s]. This is likely to be an implementation error.",
                        annotation.value(), annotation.annotationType().getName());
                throw new WebApplicationException(Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorMessage).build());
            }

            private void throwBadRequestTooManyValues(final PathParam annotation) {
                String errorMessage = String.format("Parameter [%s] must only contain one single value.", annotation.value());
                throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errorMessage).build());
            }

            private void throwBadRequestMissingValue(final PathParam annotation) {
                String errorMessage = String.format("Parameter [%s] must be provided.", annotation.value());
                throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errorMessage).build());
            }

            private DateTime parseDate(final PathParam annotation, final List<String> values) {
                try {
                    return DateTimeAdapter.parse(values.get(0));
                } catch (Exception e) {
                    String errorMessage = String.format("Parameter [%s] is formatted incorrectly: %s", annotation.value(), e.getMessage());
                    throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity(errorMessage).build());
                }
            }

        };
    }
}
于 2012-11-22T08:12:00.897 に答える