运行形式 javaws - jakarta.ws.rs.client 在第一个之后没有发送请求?客户端缓存?

Posted

技术标签:

【中文标题】运行形式 javaws - jakarta.ws.rs.client 在第一个之后没有发送请求?客户端缓存?【英文标题】:running form javaws - jakarta.ws.rs.client is not sending requests after first one? Client caching? 【发布时间】:2021-10-22 15:51:46 【问题描述】:

我有一个 Java 客户端应用程序,它发送 REST 请求以从 REST 服务中检索一些数据。

我正在这样设置客户端:

import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Response;
import org.glassfish.jersey.client.filter.EncodingFilter;
import org.glassfish.jersey.message.GZipEncoder;


SSLContext sslContext = SSLClientUtil.getSSLContext(getCertificate(keystore), keystorePassword);
this.client = ClientBuilder.newBuilder()
        .sslContext(sslContext)
        .hostnameVerifier(new TrustedHostnameVerifier())
        .build();
client.register(GZipEncoder.class);
client.register(EncodingFilter.class);

this.webTarget = client.target("https://example.org:443/service/rest");

并以另一种方法向服务器发出实际请求

//Get Method
Response response = this.webTarget.request().get()
// Extract the content
response.close();
// return the content

当我运行我的应用程序时,我第一次运行 Get-Method 时,服务返回响应并且我得到我的响应......但在第一次之后。请求甚至没有发送到服务器(我在客户端的 eth 接口上看不到网络流量。

似乎客户端以某种方式缓存了响应并且没有再次发送请求。 我已经看到服务器发送带有标题 Cache-Control: private 的响应。 如何说服我的客户再次发送实际请求(如果)不使用任何缓存响应?

注意:我已经尝试在我的客户端中设置标题缓存控制指令......但仍然没有使用方法(this.webTarget.request().get())发送请求。

更新: 经过一些测试后,java 客户端应用程序似乎作为 javaws 应用程序运行。如果从调试器(没有 javaws)启动,则行为符合预期。以 javaws 运行它,请求被缓存。 这似乎类似于条目here,其中javaws 应用程序使用URLConnection 检索缓存的文档。可以将连接设置为不缓存。jakarta.ws.rs / jersey 如何做到这一点?

【问题讨论】:

【参考方案1】:

概述

我试图重现你的问题并且

创建了一个简单的 Spring Boot 演示 REST 服务 它始终返回当前时间戳,因此缓存值等。 会很容易识别。 启动 Spring Boot 演示应用程序 创建了一个使用 JAX-RS 和 Jersey 的 swing 演示客户端 编写了一个简单的 JNLP 文件 通过 gradle 构建了一个胖罐 (自)签了肥罐 将 jnlp 文件复制到 build/libs 文件夹 切换到 build/libs 文件夹并由 javaws Test.jnlp 启动

Java web start 客户端的输出:

总结

我使用了来自 maven Central 的最新 Jersey 依赖项。测试确实有效,每个请求调用都从 REST 服务获取新数据(即使在服务器端启用了缓存)。我在 Linux 机器上使用 Java Webstart“icedtea-web 1.8 (1.8)”进行了测试。

也许你的库已经过时了,你应该像我的测试文件一样简单地更新你的依赖项?您可以简单地使用您的javaws 安装尝试以下示例并采用必要的部分。

如果这些示例也确实产生了您的问题,那么根本原因似乎是您的 webstart /Java 安装 - 也许更新会有所帮助。

来源

JaxRSTestApplication + Gradle 设置

plugins 
    id 'java'
    id 'java-library'
    id 'eclipse'


group = 'com.example.demo'
version = '1.0.0'
sourceCompatibility = '11'

repositories 
    mavenCentral()


dependencies 

    implementation group: 'org.glassfish.jersey.core', name: 'jersey-client', version: '3.0.2'
    implementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '3.0.2'
    implementation group: 'jakarta.activation', name: 'jakarta.activation-api', version: '2.0.1'



task fatJar(type: Jar) 
    archiveClassifier = 'fat'
    
    duplicatesStrategy = 'exclude'

    from sourceSets.main.output
    
    manifest 
        attributes 'Main-Class': 'com.example.demo.JaxRSTestApplication'
        attributes 'Application-Name': 'JaxRSTestApplication'
    

    dependsOn configurations.runtimeClasspath
    from 
        configurations.runtimeClasspath.findAll  it.name.endsWith('jar') .collect  zipTree(it) 
    

package com.example.demo;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;

import org.glassfish.jersey.client.filter.EncodingFilter;
import org.glassfish.jersey.message.GZipEncoder;

import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Response;

public class JaxRSTestApplication 

    private Client client;
    private WebTarget webTarget;
    private JTextArea textArea;

    public static void main(String[] args) 
        new JaxRSTestApplication().start();
    

    private class FetchDataAction extends AbstractAction
        
        private static final long serialVersionUID = 1L;

        private FetchDataAction() 
            putValue(Action.NAME, "fetch data");
        

        @Override
        public void actionPerformed(ActionEvent e) 
            fetchData();
        
        
    
    
    private void start() 
        JFrame frame = new JFrame();
        textArea = new JTextArea();
        JButton reloadButton = new JButton(new FetchDataAction());
        frame.add(textArea,BorderLayout.CENTER);
        frame.add(reloadButton, BorderLayout.SOUTH);

        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setSize(new Dimension(400,400));
        frame.setVisible(true);
        
        this.client = ClientBuilder.newBuilder().build();
        client.register(GZipEncoder.class);
        client.register(EncodingFilter.class);

        this.webTarget = client.target("http://localhost:8080/simple");

        fetchData();
        fetchData();
    

    private void fetchData() 
        // Get Method
        Response response = this.webTarget.request().get();
        // Extract the content
        String result = response.readEntity(String.class);
        textArea.setText(textArea.getText()+"\n"+"read:" + result);

        response.close();

    

JNLP 文件

<?xml version="1.0" encoding="utf-8"?> 
<jnlp spec="1.0+" codebase="./">
    <information>
        <title>Jnlp Testing</title>
        <vendor>de-jcup</vendor>
        <homepage href="http://localhost:8080/" />
        <description>Just a test application</description>
    </information>
    <security>
        <all-permissions/>
    </security>
    <resources>
        <j2se version="1.8+" />
        <jar href="demo-1.0.0-fat.jar" />
    </resources>
    <application-desc main-class="com.example.demo.JaxRSTestApplication" />
</jnlp>

Spring REST 演示服务 + Gradle 设置

plugins 
    id 'org.springframework.boot' version '2.6.0-SNAPSHOT'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
    id 'java-library'
    id 'eclipse'


group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories 
    mavenCentral()
    maven  url 'https://repo.spring.io/milestone' 
    maven  url 'https://repo.spring.io/snapshot' 


dependencies 
    api 'org.springframework.boot:spring-boot-starter-web'

package com.example.springrestdemo;

import java.util.concurrent.TimeUnit;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class SpringRestDemoApplication 

    public static void main(String[] args) 
        SpringApplication.run(SpringRestDemoApplication.class, args);
    
    
    @RestController
    public class SimpleController 

        private long secondWeWantTobeCached = 3600;

        @GetMapping(value = "/simple", produces = MediaType.APPLICATION_JSON_VALUE)
        public ResponseEntity<StringJsonObject> simpleResult() 
            return ResponseEntity.ok().cacheControl(CacheControl.maxAge(secondWeWantTobeCached, TimeUnit.SECONDS).cachePublic().noTransform())
                    .body(new StringJsonObject("Timestamp:" + System.currentTimeMillis()));
        

        public class StringJsonObject 

            private String content;

            public StringJsonObject(String content) 
                this.content = content;
            

            public String getContent() 
                return content;
            
        
    


【讨论】:

我明天必须跟进这件事 - 但服务是否以“Cache-Control: private”响应。我目前将此表示为问题。不幸的是,我只能自己更改应用程序。 当您使用给定的 Sprint Boot 演示应用程序时,您也可以使用服务器输出并更改缓存控制。您可以通过 ./gradlew bootRun 直接启动 spring 应用程序 - 当您有可用的 gradle 包装器时。如果您不熟悉 gradle,请查看 spring.io/guides/gs/gradle。 当然,如果我将服务器响应更改为 cache-control=no-store 而不是 private,它的行为会有所不同。但如前所述,我无法更改服务器。 jersey 中必须有一种方法可以告诉客户端必须重新验证响应而不是缓存。 我之前的评论仅用于测试,如果您的期望(泽西岛的缓存处理是问题)是 100% 正确的。我建议使用我的测试场景并检查您的期望是否正确,因为我不确定这是核心问题。在阅读eclipse-ee4j.github.io/jersey.github.io/documentation/latest/… 时,我没有看到任何关于客户端缓存的条目。【参考方案2】:

准确显示您如何在客户端中设置 Cache-Control 指令。这很重要,因为有许多指令及其组合。仔细阅读规范,因为指令名称并不总能传达其机制的实际工作原理。

您是否使用过指令no-storeno-cache

【讨论】:

我试过 requestContext.getHeaders().add("Cache-Control", "max-age=0");或 requestContext.getHeaders().add("Cache-Control", "no-cache");以及无商店。但问题是,在第一个请求之后,甚至都没有发送请求。

以上是关于运行形式 javaws - jakarta.ws.rs.client 在第一个之后没有发送请求?客户端缓存?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建JAR文件?如何运行.jar形式的Java程序?

用 taskkill 轻柔地结束 javaw.exe 进程

如何在开发时部署和运行前后端分离的JavaWe

为啥 javaw 加载文件而不是 eclipse.exe?

javaw命令不支持特殊字符[关闭]

win7下安装oracle9i运行setup.exe启动后提示Javaw.exe停止工作是啥情况造成的。谢谢