运行形式 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-store
或no-cache
?
【讨论】:
我试过 requestContext.getHeaders().add("Cache-Control", "max-age=0");或 requestContext.getHeaders().add("Cache-Control", "no-cache");以及无商店。但问题是,在第一个请求之后,甚至都没有发送请求。以上是关于运行形式 javaws - jakarta.ws.rs.client 在第一个之后没有发送请求?客户端缓存?的主要内容,如果未能解决你的问题,请参考以下文章