Java RMI传输非序列化Issue类问题求解
解决Jira Issue类无法通过Java RMI传输的问题
问题背景
通过Java RMI传输Jira REST Client API中的com.atlassian.jira.rest.client.api.domain.Issue类时,因该类未实现Serializable接口,抛出以下序列化异常:
java.rmi.UnmarshalException: error unmarshalling return; nested exception is: java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.atlassian.jira.rest.client.api.domain.Issue
可行解决方案
方法一:自定义可序列化的Issue DTO类
最稳妥的方式是创建包含业务所需字段的自定义DTO类,实现Serializable接口,在服务端将Jira的Issue对象转换为DTO后再通过RMI传输。
1. 定义IssueDto类
import java.io.Serializable; public class IssueDto implements Serializable { private static final long serialVersionUID = 1L; private String key; private String summary; private String statusName; private String reporterAccountId; public IssueDto(String key, String summary, String statusName, String reporterAccountId) { this.key = key; this.summary = summary; this.statusName = statusName; this.reporterAccountId = reporterAccountId; } // 省略getter方法 public String getKey() { return key; } public String getSummary() { return summary; } public String getStatusName() { return statusName; } public String getReporterAccountId() { return reporterAccountId; } }
2. 修改服务端接口JiraConnection
import java.util.List; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.concurrent.TimeoutException; public interface JiraConnection extends Remote { // 替换原方法为返回DTO列表 public List<IssueDto> getIssuesFromJqlSearch(String jqlSearch) throws RemoteException, TimeoutException; // 保留其他原有方法... }
3. 修改服务端实现类JiraServant的对应方法
import java.util.stream.Collectors; public class JiraServant extends UnicastRemoteObject implements JiraConnection { // 原有代码不变... @Override public List<IssueDto> getIssuesFromJqlSearch(String jqlSearch) throws RemoteException, TimeoutException { try { final SearchResult searchResult = getJiraRestClient().getSearchClient() .searchJql(jqlSearch) .get(5000, TimeUnit.SECONDS); // 将Jira Issue转换为自定义DTO return searchResult.getIssues().stream() .map(issue -> new IssueDto( issue.getKey(), issue.getSummary(), issue.getStatus().getName(), issue.getReporter() != null ? issue.getReporter().getAccountId() : null )) .collect(Collectors.toList()); } catch (TimeoutException e) { System.err.println("jira rest client timeout from jql search error. cause: " + e.getMessage()); throw e; } catch (Exception e) { System.err.println("jira rest client get issue from jql search error. cause: " + e.getMessage()); return Collections.emptyList(); } } }
4. 修改客户端代码使用DTO
public class Main { public static void main(String[] args) { System.out.println("=== === RMI Client === ==="); try{ String url = "rmi://localhost:5097/jira-api"; JiraConnection jiraConnection = (JiraConnection) Naming.lookup(url); String jqlSearch = "project = 'SDR' AND assignee = 627cba6c6ba8640069cf1faa AND status IN (Open) ORDER BY created DESC"; try { List<IssueDto> results = jiraConnection.getIssuesFromJqlSearch(jqlSearch); for(IssueDto issueDto: results){ String reporterAccountId = issueDto.getReporterAccountId(); System.out.print("Reporter: "+ jiraConnection.getUser(reporterAccountId)); System.out.println(", Key: "+issueDto.getKey() + ", Title: "+issueDto.getSummary() + ", Status: " + issueDto.getStatusName()); } System.out.println("Found "+results.size() +" open issues"); } catch (TimeoutException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } }catch(RemoteException | MalformedURLException e){ System.out.println("Failed to connect to server"); System.out.println("Error Message : "+e); } catch (NotBoundException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } } }
方法二:在服务端直接处理业务逻辑,返回处理结果
如果客户端仅需要展示特定信息,可直接在服务端完成Issue的字段提取与逻辑处理,返回字符串或简单数据结构,避免传输整个对象:
修改服务端接口与实现
// JiraConnection接口新增/替换方法 public interface JiraConnection extends Remote { public List<String> getIssueDetailsFromJqlSearch(String jqlSearch) throws RemoteException, TimeoutException; } // JiraServant实现 @Override public List<String> getIssueDetailsFromJqlSearch(String jqlSearch) throws RemoteException, TimeoutException { try { final SearchResult searchResult = getJiraRestClient().getSearchClient() .searchJql(jqlSearch) .get(5000, TimeUnit.SECONDS); return searchResult.getIssues().stream() .map(issue -> { String reporter = issue.getReporter() != null ? getUser(issue.getReporter().getAccountId()) : "Unknown"; return String.format("Reporter: %s, Key: %s, Title: %s, Status: %s", reporter, issue.getKey(), issue.getSummary(), issue.getStatus().getName()); }) .collect(Collectors.toList()); } catch (Exception e) { System.err.println("jira rest client get issue details error. cause: " + e.getMessage()); return Collections.emptyList(); } }
客户端直接打印结果
List<String> issueDetails = jiraConnection.getIssueDetailsFromJqlSearch(jqlSearch); issueDetails.forEach(System.out::println);
方法三:动态序列化(不推荐)
通过第三方库(如Jackson、XStream)将Issue对象序列化为JSON/XML字符串,传输后再反序列化,但需处理Jira Issue类的复杂依赖与私有字段,稳定性差,不建议用于生产环境。
内容的提问来源于stack exchange,提问作者lordyhas
相关产品推荐
相关产品推荐

