Skip to main content

19 posts tagged with "java"

View All Tags

解决 Spring Boot 3.5.0 后 Jasypt 无法解析环境变量中的加密字符串问题

· 6 min read
orange
programmer on jvm platform

当 Spring Boot 项目升级到 3.5.0 及更高版本后,使用 jasypt-spring-boot-starter 会遇到一个问题:应用程序无法正确解析环境变量中配置的加密字符串。具体表现为,在应用程序运行期间,读取到的配置值仍然是加密后的字符串(例如 ENC(加密字符串)),而非解密后的原始值。

通过增加timeout-control以解决当tcp连接一直处于SYN_SENT状态导致java中的ldap-client的failover不工作的问题

· 8 min read
orange
programmer on jvm platform

今天遇到一个问题是当测试模拟ldap的服务端主节点挂掉的时候并在页面点击登录, 后端一直未作出响应.
这个问题的原因是因为代码中的ldap-clientfailover未生效, 通过排查发现ldap-client一直在连接ldap的主节点, 并且tcp连接一直处于SYN_SENT状态.
由于ldap-client没有默认情况下没有控制超时, 导致代码一直堵塞, 从而导致failover不工作.
下面将开始介绍具体细节以及解决方案.

基于clojure表达式实现更加灵活的数据验证

· 13 min read
orange
programmer on jvm platform

数据验证是一个非常常见的需求, 对于java项目来说, 目前jakartabean validation已经成为了java中的标准.
其自带了一些常见的数据验证注解, 例如@NotNull, @NotEmpty, @Size等.
这些注解如果遇到复杂的数据验证需求时, 就会显得力不从心. 所以需要一种更加灵活的数据验证方式.
为了满足这种需求, 我们可以通过clojure表达式来实现数据验证.
同时我们需要和现有的bean validation一起使用, 以便于满足现有的业务需求.

JDK改进项目的介绍

· 2 min read
orange
programmer on jvm platform

以下列表是截至到目前Open JDK中部分进行的对JDK的改进项目

  • Amber
  • Galahad
  • Leyden
  • Lilliput
  • Loom
  • Panama
  • Valhalla

这些改进项目的主要目的是为了改进Java的性能和开发体验. 从而使Java能够更好地适应目前的软件开发需求.
以下是这些项目的简单介绍.

当调度服务接口时报错提示cannot execute UPDATE in a read-only transaction问题解决

· 4 min read
orange
programmer on jvm platform

前端请求服务相应接口报错, 日志如下

2023-01-30 12:10:14.822  WARN 1 --- [nio-4396-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 0, SQLState: 25006
2023-01-30 12:10:14.822 ERROR 1 --- [nio-4396-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: cannot execute UPDATE in a read-only transaction
at org.springframework.security.web.authentication.AnonymousAuthen
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:121)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:684)
at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:920)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at jdk.internal.reflect.GeneratedMethodAccessor355.invoke(Unknown Source)
at com.fastonetech.computecloud.api.regional.software.controller.LaunchableAppController.lastAccessAt(LaunchableAppController.kt:56)
at com.fastonetech.computecloud.api.regional.software.service.UserSoftwareUsageServiceImpl$$EnhancerBySpringCGLIB$$1caff315.lastAccessAt(<generated>)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:407)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:654)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:562)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:40)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:448)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2380)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3212)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:453)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1362)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:99)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:40)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:344)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:475)
at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:201)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3769)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3355)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3493)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:130)
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401)
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:322)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2297)
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2565)
org.postgresql.util.PSQLException: ERROR: cannot execute UPDATE in a read-only transaction

原因

数据库采用主从架构, 由于主库宕机, Load balancer将请求转发到了从库, 从库的事务为read only导致更新失败。

后端相关代码如下

@javax.transaction.Transactional
fun lastAccessAt(id: Long, type: LaunchableAppType): UserSoftwareUsageDTO {
val userId = authService.currentAsPortal().bind().userId
val userSoftwareUsage =
getOrElseCreate(userId, id, type).copy(lastAccessAt = ZonedDateTime.now()).let(repository::save)
return UserSoftwareUsageDTO(
userSoftwareUsage.appId,
userSoftwareUsage.appType,
userSoftwareUsage.lastAccessAt,
userSoftwareUsage.collected
)
}

其采用的javax@Transactional进行事务控制.
该注解没有提供显式指定数据库事务的读写行为相关属性, 是否是只读或者写入只能由由数据库的默认行为决定.

PostgreSQL中, 可以通过以下命令来查看默认的读写行为

show default_transaction_read_only;
-- return true if default transaction is read only, false otherwise

解决方案

  1. 数据库主机宕机时, 从库升级为主库. 这样可以确保数据库读写不受影响。
  2. java侧需要采用可以显式控制事务是否为readonly的框架, 如spring的@Transactional注解, 该注解提供了readOnly属性, 可以显式控制事务的读写行为。这样可以不隐式依赖数据库的默认行为, 从而在创建事务时提前发现问题。

备注

Spring Boot项目启用native构建后由于序列化对象缺少ReflectionHints导致json数据为'{}'

· One min read
orange
programmer on jvm platform

基于graalvmspring boot项目打包好后调用/scheduling/api/v1/taskDefinitions接口返回如下数据, 其中triggerStrategy字段是空json对象

[
{
"id": "BILLING_SYNC_FOR_DEPLOYMENT_127",
"triggerStrategy": {},
"tags": {
"EXECUTOR": "com.fastonetech.billing.sync.scheduling.BillingSyncTaskExecutor"
},
"variables": {
"SOURCE_RCLONE_CONFIG": "TENCENT_ap-beijing",
"SOURCE_BUCKET": "cheng1201-1310454728",
"SOURCE_PATH": "",
"TARGET_RCLONE_CONFIG": "aggregation",
"TARGET_BUCKET": "billing-aggregation",
"TARGET_PATH": "TENCENT/ap-beijing/cheng1201-1310454728"
}
}
]

通过引入防御性复制以避免ConcurrentModificationException

· 2 min read
orange
programmer on jvm platform

consul中修改相关服务的配置时引发ConcurrentModificationException并导致协程任务异常退出.
相关报错如下:

2022-11-24 10:08:27.954  INFO 1 --- [TaskScheduler-1] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-config/mgmt-scheduler/'}, BootstrapPropertySource {name='bootstrapProperties-config/application/'}]
2022-11-24 10:08:27.968 INFO 1 --- [TaskScheduler-1] o.s.boot.SpringApplication : No active profile set, falling back to 1 default profile: "default"
2022-11-24 10:08:27.979 INFO 1 --- [TaskScheduler-1] o.s.boot.SpringApplication : Started application in 0.244 seconds (JVM running for 7930.38)
Exception in thread "DefaultDispatcher-worker-6" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
at com.fastonetech.scheduling.core.ResourceDispatcher$Companion$of$1$1.invokeSuspend(ResourceDispatcher.kt:40)
at com.fastonetech.scheduling.core.ResourceDispatcher$Companion$of$1$1.invoke(ResourceDispatcher.kt)
at com.fastonetech.scheduling.core.ResourceDispatcher$Companion$of$1$1.invoke(ResourceDispatcher.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264)
at com.fastonetech.scheduling.core.ResourceDispatcher$Companion$of$1.dispatch(ResourceDispatcher.kt:16)
at com.fastonetech.scheduling.core.ResourceDispatcher$Companion$of$3.schedule(ResourceDispatcher.kt:32)
at com.fastonetech.scheduling.core.ResourceDispatcher$Companion$of$3.schedule(ResourceDispatcher.kt:27)
at com.fastonetech.scheduling.core.ResourceDispatcher$Companion$of$1$1$2$1.invokeSuspend(ResourceDispatcher.kt:19)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@19b8b4b6, Dispatchers.IO]

原因

这个问题的原因是被修改的配置映射到了代码中被@ConfigurationProperties注解的类中的一个List类型的属性.
该属性被修改时恰好协程任务正在遍历该属性, 从而导致ConcurrentModificationException异常.

解决方案

每次获取该属性时都进行一次防御性复制, 从而避免ConcurrentModificationException异常

参考资料

grpc服务protobuf编译报错Tried to write the same file twice.

· One min read
orange
programmer on jvm platform

新增quota_usage.proto文件后编译失败, 输出以下错误

[ERROR] PROTOC FAILED: com/fastonetech/contract/computecloud/deploy/v2/QuotaUsage.java: Tried to write the same file twice.
[libprotobuf WARNING ../../../../../src/google/protobuf/compiler/java/java_file.cc:232] cmdb/v1/cmdb.proto: The file's outer class name, "Cmdb", matches the name of one of the types declared inside it when case is ignored. This can cause compilation issues on Windows / MacOS. Please either rename the type or use the java_outer_classname option to specify a different outer class name for the .proto file to be safe.