如何使用jberet-ui创建基于日历的时间表?

huangapple 未分类评论50阅读模式
英文:

How to create a calendar-based schedule using jberet-ui?

问题

我正在使用从 https://github.com/jberet/jberet-ui.git 的主分支构建的 jberet-ui(嵌入在一个阴影化的 war 中),其与版本 1.4.0.Final 的 jberet-rest-api、jberet-rest-common、jberet-schedule-executor 以及 jberet-schedule-timer 作为依赖项。

当我尝试创建基于日历的调度时,API 返回 400 响应 如何使用jberet-ui创建基于日历的时间表?,页面底部显示消息为“无法为作业安排作业执行:ag-insurance-import-lisa-subscriptions。”,并在响应正文中出现以下文本:

Unrecognized field "hour" (class javax.ejb.ScheduleExpression),未标记为可忽略

这个 javax.ejb.ScheduleExpression 应该如何进行反序列化?它似乎不像一个简单的POJO,无法直接绑定到 JSON 模型上,而且在 jberet-rest* 项目中我找不到任何反序列化器。我需要自己提供 JSON 的(反)序列化器吗?

英文:

Im using jberet-ui (built from master branch of https://github.com/jberet/jberet-ui.git) embedded in a shaded war with jberet-rest-api, jberet-rest-common, jberet-schedule-executor and jberet-schedule-timer version 1.4.0.Final as dependencies.

When I try to create a calendar-based schedule, I get a 400 response from the api 如何使用jberet-ui创建基于日历的时间表? with a message 'Failed to schedule job execution for job: ag-insurance-import-lisa-subscriptions.' displayed in the bottom of the page, and the text

Unrecognized field "hour" (class javax.ejb.ScheduleExpression), not marked as ignorable

in the response body.

How is this javax.ejb.ScheduleExpression supposed to be deserialized? It does not seem like a simple pojo that could be simply bound to a json model, and I couldn't find any deserializer in the jberet-rest* projects.
Am I supposed to provide my own json (de)serializers?

答案1

得分: 0

以下是翻译好的内容:

有一个带有jberet-ui的示例批处理应用程序(scheduleTimer),你可能想要查看一下。

我以前从未见过这个错误。它可能与不同的jackson库版本(用于json绑定)之间的一些更改相关。您可能希望尝试与上述jberet示例项目中使用的jackson-*依赖项的确切版本相同。

问题代码行位于JobScheduleConfig类中。

您能分享一下来自WildFly server.log的错误详细信息和堆栈跟踪吗?

英文:

There is a sample batch app (scheduleTimer) with jberet-ui, and you may want to check it out.

I've never seen this error before. It could be related to some changes across different jackson library versions (used for json binding). You may want to try the exact version of jackson-* dependencies as used in the above jberet sample project.

The line of code in question is in JobScheduleConfig class.

Can you share error details and stacktrace from WildFly server.log?

答案2

得分: 0

以下是您要翻译的内容:

为了解决这个问题,目前可以使用以下方法。

依赖项:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>8.0.1</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.jberet</groupId>
    <artifactId>jberet-rest-commons</artifactId>
    <version>1.4.0.Final</version>
</dependency>
<dependency>
    <groupId>org.jberet</groupId>
    <artifactId>jberet-schedule-executor</artifactId>
    <version>1.4.0.Final</version>
</dependency>
<dependency>
    <groupId>org.jberet</groupId>
    <artifactId>jberet-schedule-timer</artifactId>
    <version>1.4.0.Final</version>
</dependency>
<dependency>
    <groupId>org.jberet</groupId>
    <artifactId>jberet-rest-api</artifactId>
    <version>1.4.0.Final</version>
</dependency>

然后创建一个 JSON 反序列化器:

public class ScheduleExpressionDeserializer extends JsonDeserializer<ScheduleExpression> {

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE;

    @Override
    public ScheduleExpression deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        ScheduleExpression scheduleExpression = new ScheduleExpression();

        // 负载示例:
        // {"year":"1","month":"2","dayOfMonth":"3","dayOfWeek":"4",
        // "hour":"5","minute":"6","start":"2020-05-04T08:10:00.000Z",
        // "end":"2020-06-05T08:12:00.000Z","timezone":"Africa/Blantyre"}
        TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);

        Optional.ofNullable(treeNode.get("year"))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::year);

        Optional.ofNullable(treeNode.get("month"))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::month);

        Optional.ofNullable(treeNode.get("dayOfMonth"))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::dayOfMonth);

        Optional.ofNullable(treeNode.get("dayOfWeek"))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::dayOfWeek);

        Optional.ofNullable(treeNode.get("hour"))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::hour);

        Optional.ofNullable(treeNode.get("minute"))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::minute);


        Optional.ofNullable(treeNode.get("start"))
                .flatMap(this::parseDateTimeNode)
                .ifPresent(scheduleExpression::start);

        Optional.ofNullable(treeNode.get("end"))
                .flatMap(this::parseDateTimeNode)
                .ifPresent(scheduleExpression::end);

        Optional.ofNullable(treeNode.get("timezone"))
                .map(TreeNode::asToken)
                .map(JsonToken::asString)
                .ifPresent(scheduleExpression::timezone);

        return scheduleExpression;
    }

    private Optional<Integer> parseIntegerNode(TreeNode node) {
        if (node instanceof TextNode) {
            TextNode textNode = (TextNode) node;
            return Optional.of(textNode)
                    .map(TextNode::asInt);
        } else {
            return Optional.empty();
        }
    }

    private Optional<Date> parseDateTimeNode(TreeNode node) {
        if (node instanceof TextNode) {
            TextNode textNode = (TextNode) node;
            return Optional.of(textNode)
                    .map(TextNode::asText)
                    .map(s -> OffsetDateTime.parse(s, dateTimeFormatter))
                    .map(OffsetDateTime::toInstant)
                    .map(Date::from);
        } else {
            return Optional.empty();
        }
    }
}

并在 JAX-RS 提供的 ObjectMapper 中使用它:

@Provider
public class JacksonMapperResolver implements ContextResolver<ObjectMapper> {

    @Override
    public ObjectMapper getContext(Class<?> type) {
        ObjectMapper mapper = new ObjectMapper();

        SimpleModule simpleModule = new SimpleModule("Custom deserializers");
        simpleModule.addDeserializer(ScheduleExpression.class, new ScheduleExpressionDeserializer());
        mapper.registerModule(simpleModule);
        return mapper;
    }
}
英文:

To work around this issue, currently, the following works.

war dependencies:

     &lt;dependency&gt;
        &lt;groupId&gt;javax&lt;/groupId&gt;
        &lt;artifactId&gt;javaee-api&lt;/artifactId&gt;
        &lt;version&gt;8.0.1&lt;/version&gt;
        &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.jberet&lt;/groupId&gt;
        &lt;artifactId&gt;jberet-rest-commons&lt;/artifactId&gt;
        &lt;version&gt;1.4.0.Final&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.jberet&lt;/groupId&gt;
        &lt;artifactId&gt;jberet-schedule-executor&lt;/artifactId&gt;
        &lt;version&gt;1.4.0.Final&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.jberet&lt;/groupId&gt;
        &lt;artifactId&gt;jberet-schedule-timer&lt;/artifactId&gt;
        &lt;version&gt;1.4.0.Final&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.jberet&lt;/groupId&gt;
        &lt;artifactId&gt;jberet-rest-api&lt;/artifactId&gt;
        &lt;version&gt;1.4.0.Final&lt;/version&gt;
    &lt;/dependency&gt;

Then create a json deserializer:

 public class ScheduleExpressionDeserializer extends JsonDeserializer&lt;ScheduleExpression&gt; {

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE;

    @Override
    public ScheduleExpression deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        ScheduleExpression scheduleExpression = new ScheduleExpression();

        // Payload example:
        // {&quot;year&quot;:&quot;1&quot;,&quot;month&quot;:&quot;2&quot;,&quot;dayOfMonth&quot;:&quot;3&quot;,&quot;dayOfWeek&quot;:&quot;4&quot;,
        // &quot;hour&quot;:&quot;5&quot;,&quot;minute&quot;:&quot;6&quot;,&quot;start&quot;:&quot;2020-05-04T08:10:00.000Z&quot;,
        // &quot;end&quot;:&quot;2020-06-05T08:12:00.000Z&quot;,&quot;timezone&quot;:&quot;Africa/Blantyre&quot;}
        TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);

        Optional.ofNullable(treeNode.get(&quot;year&quot;))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::year);

        Optional.ofNullable(treeNode.get(&quot;month&quot;))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::month);

        Optional.ofNullable(treeNode.get(&quot;dayOfMonth&quot;))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::dayOfMonth);

        Optional.ofNullable(treeNode.get(&quot;dayOfWeek&quot;))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::dayOfWeek);

        Optional.ofNullable(treeNode.get(&quot;hour&quot;))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::hour);

        Optional.ofNullable(treeNode.get(&quot;minute&quot;))
                .flatMap(this::parseIntegerNode)
                .ifPresent(scheduleExpression::minute);


        Optional.ofNullable(treeNode.get(&quot;start&quot;))
                .flatMap(this::parseDateTimeNode)
                .ifPresent(scheduleExpression::start);

        Optional.ofNullable(treeNode.get(&quot;end&quot;))
                .flatMap(this::parseDateTimeNode)
                .ifPresent(scheduleExpression::end);

        Optional.ofNullable(treeNode.get(&quot;timezone&quot;))
                .map(TreeNode::asToken)
                .map(JsonToken::asString)
                .ifPresent(scheduleExpression::timezone);

        return scheduleExpression;
    }

    private Optional&lt;Integer&gt; parseIntegerNode(TreeNode node) {
        if (node instanceof TextNode) {
            TextNode textNode = (TextNode) node;
            return Optional.of(textNode)
                    .map(TextNode::asInt);
        } else {
            return Optional.empty();
        }
    }


    private Optional&lt;Date&gt; parseDateTimeNode(TreeNode node) {
        if (node instanceof TextNode) {
            TextNode textNode = (TextNode) node;
            return Optional.of(textNode)
                    .map(TextNode::asText)
                    .map(s -&gt; OffsetDateTime.parse(s, dateTimeFormatter))
                    .map(OffsetDateTime::toInstant)
                    .map(Date::from);
        } else {
            return Optional.empty();
        }
    }
}

And use it in a jax-rs-provided ObjectMapper:

@Provider
public class JacksonMapperResolver implements ContextResolver&lt;ObjectMapper&gt; {

    @Override
    public ObjectMapper getContext(Class&lt;?&gt; type) {
        ObjectMapper mapper = new ObjectMapper();

        SimpleModule simpleModule = new SimpleModule(&quot;Custom deserializers&quot;);
        simpleModule.addDeserializer(ScheduleExpression.class, new ScheduleExpressionDeserializer());
        mapper.registerModule(simpleModule);
        return mapper;
    }
}

huangapple
  • 本文由 发表于 2020年5月19日 20:23:11
  • 转载请务必保留本文链接:https://java.coder-hub.com/61890981.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定