From a28cba807f60997acc9d09c227d6702b903b402a Mon Sep 17 00:00:00 2001 From: Florent THOMAS Date: Wed, 3 Jun 2020 01:02:36 +0200 Subject: [PATCH] Use jsonrpc to call report for v>=11 --- pom.xml | 114 ++++---- .../java/com/odoojava/api/ReportAdapter.java | 88 ++++-- src/main/java/com/odoojava/api/Session.java | 261 ++++++++++++------ .../com/odoojava/api/ReportAdapterTest.java | 11 +- 4 files changed, 314 insertions(+), 160 deletions(-) diff --git a/pom.xml b/pom.xml index 86d4922..10c1a68 100644 --- a/pom.xml +++ b/pom.xml @@ -1,54 +1,70 @@ - - 4.0.0 + + 4.0.0 - com.odoojava - odoo-java-api - 3.1.2 - jar + com.odoojava + odoo-java-api + 3.1.2 + jar - odoo-java-api - http://maven.apache.org + odoo-java-api + http://maven.apache.org - - UTF-8 - 1.8 - 1.8 - + + UTF-8 + 1.8 + 1.8 + - - - junit - junit - 4.12 - test - - - org.apache.xmlrpc - xmlrpc-client - 3.1.3 - - - org.assertj - assertj-core - 3.3.0 - test - - - org.mock-server - mockserver-netty - 3.10.2 - test - - - - odoo-java - http://odoo-java.com - - - https://github.com/odoo-java/odoo-java-api - - - https://github.com/odoo-java/odoo-java-api/issues - + + + junit + junit + 4.12 + test + + + org.apache.xmlrpc + xmlrpc-client + 3.1.3 + + + org.assertj + assertj-core + 3.3.0 + test + + + org.mock-server + mockserver-netty + 3.10.2 + test + + + javax.xml.bind + jaxb-api + 2.4.0-b180830.0359 + + + org.eclipse.lsp4j + org.eclipse.lsp4j.jsonrpc + 0.9.0 + + + com.github.briandilley.jsonrpc4j + jsonrpc4j + 1.5.1 + + + + odoo-java + http://odoo-java.com + + + https://github.com/odoo-java/odoo-java-api + + + https://github.com/odoo-java/odoo-java-api/issues + diff --git a/src/main/java/com/odoojava/api/ReportAdapter.java b/src/main/java/com/odoojava/api/ReportAdapter.java index 97670e2..95090c1 100644 --- a/src/main/java/com/odoojava/api/ReportAdapter.java +++ b/src/main/java/com/odoojava/api/ReportAdapter.java @@ -21,8 +21,12 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.Arrays; import java.util.Date; import java.util.HashMap; @@ -34,8 +38,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.xml.bind.DatatypeConverter; + import org.apache.xmlrpc.XmlRpcException; +import com.googlecode.jsonrpc4j.JsonRpcHttpClient; import com.odoojava.api.Field.FieldType; import com.odoojava.api.helpers.FilterHelper; @@ -44,9 +51,8 @@ * Main class for managing reports with the server. * * @author Florent THOMAS - * @param: reportListCache - * . Consider Object part that will be set with name/model/type of - * the Odoo report + * @param: reportListCache . Consider Object part that will be set with + * name/model/type of the Odoo report */ public class ReportAdapter { @@ -54,6 +60,9 @@ public class ReportAdapter { private Version serverVersion; private Object[] report; private String reportName; + private String reportModel; + private String reportMethod; + private ObjectAdapter objectReportAdapter; /** * @ @@ -66,6 +75,7 @@ public ReportAdapter(Session session) throws XmlRpcException { this.serverVersion = session.getServerVersion(); try { getReportList(); + } catch (OdooApiException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -73,32 +83,66 @@ public ReportAdapter(Session session) throws XmlRpcException { } /* - * Method listing the available report and their type Purpose is to use the - * list later to check the existence of the report and its type. Appropriate - * methods will be possible regarding the type + * Method listing the available report and their type Purpose is to use the list + * later to check the existence of the report and its type. Appropriate methods + * will be possible regarding the type */ private void getReportList() throws XmlRpcException, OdooApiException { reportListCache.clear(); - ObjectAdapter objectAd = this.session.getObjectAdapter("ir.actions.report.xml"); + objectReportAdapter = this.session.getObjectAdapter(this.getReportModel()); FilterCollection filters = new FilterCollection(); - String[] report_tuple = new String[] { "report_name", "model", "name", "report_type" }; - RowCollection reports = objectAd.searchAndReadObject(filters, report_tuple); + String[] report_tuple = new String[] { "id", "report_name", "model", "name", "report_type" }; + RowCollection reports = objectReportAdapter.searchAndReadObject(filters, report_tuple); reports.forEach(report -> { - Object[] repName = new Object[] { report.get("name"), report.get("model"), report.get("report_type") }; + Object[] repName = new Object[] { report.get("name"), report.get("model"), report.get("report_type"), + report.get("id") }; reportListCache.put(report.get("report_name").toString(), repName); }); } + /** + * This method is fully inspire by + * https://github.com/OCA/odoorpc/blob/master/odoorpc/report.py#L113 from + * https://github.com/sebalix + * + * @return string representing the reportModel regarding the version + */ + public String getReportModel() { + reportModel = "ir.actions.report"; + if (this.serverVersion.getMajor() < 11) { + reportModel = "ir.actions.report.xml"; + } + return reportModel; + } + + public String getReportMethod() { + reportMethod = "render"; + if (this.serverVersion.getMajor() < 11) { + reportModel = "render_report"; + } + return reportMethod; + } + /** * @param reportName * @param ids * @return - * @throws XmlRpcException - * @throws OdooApiException + * @throws Throwable */ - public byte[] getReportAsByte(String reportName, Object[] ids) throws XmlRpcException, OdooApiException { + public byte[] getPDFReportAsByte(String reportName, Object[] ids) throws Throwable { checkReportName(reportName); - byte[] reportDatas = session.executeReportService(reportName, ids); + byte[] reportDatas; + if (this.serverVersion.getMajor() < 11) { + reportDatas = session.executeReportService(reportName, this.getReportMethod(), ids); + } else { + ArrayList reportParams = new ArrayList(); + reportParams.add( getReportID()); + reportParams.add( ids); + Object[] result = session.call_report_jsonrpc(getReportModel(), getReportMethod(), reportParams); + + String pdf_string= (String) result[0]; + reportDatas = pdf_string.getBytes(StandardCharsets.ISO_8859_1); + } return reportDatas; } @@ -107,8 +151,7 @@ public byte[] getReportAsByte(String reportName, Object[] ids) throws XmlRpcExce * Method to prepare the report to be generated Make some usefull tests * regarding the * - * @param reportName: - * can be found in Technical > report > report + * @param reportName: can be found in Technical > report > report * @throws OdooApiException * @throws XmlRpcException */ @@ -142,19 +185,30 @@ public String getReportType() { return this.report[2].toString(); } + public Integer getReportID() { + return Integer.valueOf(this.report[3].toString()); + } + public String PrintReportToFileName(Object[] ids) throws IOException, XmlRpcException, OdooApiException { File tmp_file = File.createTempFile("odoo-" + report[1].toString() + "-", getReportType().replace("qweb-", "."), null); - byte[] report_bytes = getReportAsByte(reportName, ids); + byte[] report_bytes; FileOutputStream report_stream = new FileOutputStream(tmp_file); try { + report_bytes = getPDFReportAsByte(reportName, ids); + + report_stream.write(report_bytes); + } catch (Throwable e) { + // TODO Auto-generated catch block + e.printStackTrace(); } finally { report_stream.close(); } return tmp_file.getAbsolutePath().toString(); } + } diff --git a/src/main/java/com/odoojava/api/Session.java b/src/main/java/com/odoojava/api/Session.java index d3ec3c3..b3cd95f 100644 --- a/src/main/java/com/odoojava/api/Session.java +++ b/src/main/java/com/odoojava/api/Session.java @@ -20,6 +20,13 @@ import java.util.ArrayList; import javax.xml.bind.DatatypeConverter; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; @@ -27,6 +34,7 @@ import com.odoojava.api.OdooXmlRpcProxy.RPCProtocol; import com.odoojava.api.OdooXmlRpcProxy.RPCServices; +import com.googlecode.jsonrpc4j.JsonRpcHttpClient; import com.odoojava.api.OdooApiException; import java.util.Arrays; @@ -67,21 +75,45 @@ public class Session { private OdooXmlRpcProxy objectClient; private Version serverVersion; + private URL jsonurl; + private JsonRpcHttpClient jsonclient; + private Object[] login_args; + + public URL getJsonurl(String entryPoint) { + String protocol_str = ""; + switch (this.protocol) { + case RPC_HTTP: + protocol_str = "http"; + break; + + default: + protocol_str = "https"; + break; + } + + URL urljson; + try { + urljson = new URL(String.format("%s://%s:%s/%s", protocol_str, this.host, this.port, entryPoint)); + } catch (MalformedURLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + urljson = null; + } + + this.jsonurl = urljson; + return this.jsonurl; + } + /** * * Session constructor * - * @param protocol - * XML-RPC protocol to use. ex http/https. - * @param host - * Host name or IP address where the Odoo server is hosted - * @param port - * XML-RPC port number to connect to. Typically 8069. - * @param databaseName - * Database name to connect to - * @param userName - * Username to log into the Odoo server - * @param password - * Password to log into the Odoo server + * @param protocol XML-RPC protocol to use. ex http/https. + * @param host Host name or IP address where the Odoo server is hosted + * @param port XML-RPC port number to connect to. Typically 8069. + * @param databaseName Database name to connect to + * @param userName Username to log into the Odoo server + * @param password Password to log into the Odoo server + * @throws MalformedURLException */ public Session(RPCProtocol protocol, String host, int port, String databaseName, String userName, String password) { this.protocol = protocol; @@ -91,21 +123,30 @@ public Session(RPCProtocol protocol, String host, int port, String databaseName, this.userName = userName; this.password = password; this.objectClient = new OdooXmlRpcProxy(protocol, host, port, RPCServices.RPC_OBJECT); + try { + setJsonClient(); + } catch (MalformedURLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + private void setJsonClient() throws MalformedURLException { + // TODO Auto-generated method stub + JsonRpcHttpClient client = new JsonRpcHttpClient(getJsonurl("")); + jsonclient = client; + } /** * * Session constructor. Uses default http protocol to connect. * - * @param host - * Host name or IP address where the Odoo server is hosted - * @param port - * XML-RPC port number to connect to. Typically 8069. - * @param databaseName - * Database name to connect to - * @param userName - * Username to log into the Odoo server - * @param password - * Password to log into the Odoo server + * @param host Host name or IP address where the Odoo server is hosted + * @param port XML-RPC port number to connect to. Typically 8069. + * @param databaseName Database name to connect to + * @param userName Username to log into the Odoo server + * @param password Password to log into the Odoo server */ public Session(String host, int port, String databaseName, String userName, String password) { this(RPCProtocol.RPC_HTTP, host, port, databaseName, userName, password); @@ -113,8 +154,8 @@ public Session(String host, int port, String databaseName, String userName, Stri /** * Returns an initialized ObjectAdapter object for ease of reference. A - * ObjectAdapter object does type conversions and error checking before - * making a call to the server + * ObjectAdapter object does type conversions and error checking before making a + * call to the server * * @return */ @@ -123,11 +164,10 @@ public ObjectAdapter getObjectAdapter(String objectName) throws XmlRpcException, } /** - * * Starts a session on the Odoo server and saves the UserID for use in - * later calls + * * Starts a session on the Odoo server and saves the UserID for use in later + * calls * - * @throws Exception - * upon failure to connect + * @throws Exception upon failure to connect */ public void startSession() throws Exception { @@ -149,9 +189,9 @@ public void startSession() throws Exception { private void checkVersionCompatibility() throws XmlRpcException, OdooApiException { - if (this.getServerVersion().getMajor() < 8 || this.getServerVersion().getMajor() > 12 ) { - throw new OdooApiException( - "Only Odoo Version from v8.x to 12.x are maintained. " + "Please choose another version of the library"); + if (this.getServerVersion().getMajor() < 8 || this.getServerVersion().getMajor() > 13) { + throw new OdooApiException("Only Odoo Version from v8.x to 12.x are maintained. " + + "Please choose another version of the library"); } } @@ -159,7 +199,7 @@ private void checkVersionCompatibility() throws XmlRpcException, OdooApiExceptio /** * * @param reportName - * @return reportAdapter initialized with + * @return reportAdapter initialized with * @throws OdooApiException * @throws XmlRpcException */ @@ -181,10 +221,18 @@ void getRemoteContext() throws XmlRpcException { } int authenticate() throws XmlRpcException, Exception { - OdooXmlRpcProxy commonClient = new OdooXmlRpcProxy(protocol, host, port, RPCServices.RPC_COMMON); + // XMLRPC part + OdooXmlRpcProxy commonClient = new OdooXmlRpcProxy(protocol, host, port, RPCServices.RPC_COMMON); Object id = commonClient.execute("login", new Object[] { databaseName, userName, password }); + // JSONRPC part + try { + id = authenticate_json_rpc(); + } catch (Throwable e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } if (id instanceof Integer) { userID = (Integer) id; } else { @@ -194,6 +242,55 @@ int authenticate() throws XmlRpcException, Exception { return userID; } + private int authenticate_json_rpc() throws Throwable { + // TODO: fast and uggly implementation of json rpc, has to be reafctored in the + // future + + Map articleMapOne = new HashMap<>(); + articleMapOne.put("password", password); + articleMapOne.put("login", userName); + articleMapOne.put("db", databaseName); + +// Object[] result = call_json_rpc(, "common", "login", articleMapOne); + + jsonclient.setServiceUrl(getJsonurl("web/session/authenticate")); + + Map result = jsonclient.invoke("call", articleMapOne, HashMap.class); +// Object[] result = jsonclient.readResponse(clazz, input); +// result.get + return (int) result.get("uid"); + } + + public Object[] call_report_jsonrpc(String reportModel, String reportMethod, ArrayList args) + throws Throwable { + // TODO: fast and uggly implementation of json rpc, has to be reafctored in the + // future + + jsonclient.setServiceUrl(getJsonurl("jsonrpc")); + Map jsonparams = new HashMap<>(); + jsonparams.put("service", "object"); + jsonparams.put("method", "execute_kw"); + + ArrayList methodparams = new ArrayList<>(); + methodparams.add(databaseName); + methodparams.add(userID); + methodparams.add(password); + methodparams.add(reportModel); + methodparams.add(reportMethod); + methodparams.add( args); + +// methodparams.put("color", 8); +// methodparams.put("memo", "another note"); +// note_id = call(url, "object", "execute", DB, uid, PASS, 'note.note', 'create', args) + + jsonparams.put("args", methodparams); + + Object[] result = jsonclient.invoke("call", jsonparams, Object[].class); + + return result; + + } + void checkDatabasePresenceSafe() { // 21/07/2012 - Database listing may not be enabled (--no-database-list // or list_db=false). @@ -229,13 +326,11 @@ private synchronized static void startConnecting() { } /** - * * Get a list of databases available on a specific host and port with the - * http protocol. + * * Get a list of databases available on a specific host and port with the http + * protocol. * - * @param host - * Host name or IP address where the Odoo server is hosted - * @param port - * XML-RPC port number to connect to + * @param host Host name or IP address where the Odoo server is hosted + * @param port XML-RPC port number to connect to * @return A list of databases available for the Odoo instance * @throws XmlRpcException */ @@ -246,13 +341,10 @@ public static ArrayList getDatabaseList(String host, int port) throws Xm /** * * Get a list of databases available on a specific host and port * - * @param protocol - * Protocol to use when connecting to the RPC service ex. - * http/https - * @param host - * Host name or IP address where the Odoo server is hosted - * @param port - * XML-RPC port number to connect to + * @param protocol Protocol to use when connecting to the RPC service ex. + * http/https + * @param host Host name or IP address where the Odoo server is hosted + * @param port XML-RPC port number to connect to * @return A list of databases available for the Odoo instance * @throws XmlRpcException */ @@ -272,19 +364,15 @@ public static ArrayList getDatabaseList(RPCProtocol protocol, String hos } /** - * Executes any command on the server linked to the /xmlrpc/object service. - * All parameters are prepended by: "databaseName,userID,password" This - * method execute the command without the context parameter Its purpose is - * to be used by Odoo version prior to v10 or for v10 methods that mustn't - * use the context + * Executes any command on the server linked to the /xmlrpc/object service. All + * parameters are prepended by: "databaseName,userID,password" This method + * execute the command without the context parameter Its purpose is to be used + * by Odoo version prior to v10 or for v10 methods that mustn't use the context * - * @param objectName - * Object or model name to execute the command on - * @param commandName - * Command name to execute - * @param parameters - * List of parameters for the command. For easy of use, consider - * the OdooCommand object or ObjectAdapter + * @param objectName Object or model name to execute the command on + * @param commandName Command name to execute + * @param parameters List of parameters for the command. For easy of use, + * consider the OdooCommand object or ObjectAdapter * @return The result of the call * @throws XmlRpcException */ @@ -304,16 +392,13 @@ public Object executeCommand(final String objectName, final String commandName, /** * Executes any command on the server linked to the /xmlrpc/object service. - * parameters and Context are prepended .The context MUST NOT have been - * already passed in the parameters. + * parameters and Context are prepended .The context MUST NOT have been already + * passed in the parameters. * - * @param objectName - * Object or model name to execute the command on - * @param commandName - * Command name to execute - * @param parameters - * List of parameters for the command. For easy of use, consider - * the OdooCommand object or ObjectAdapter + * @param objectName Object or model name to execute the command on + * @param commandName Command name to execute + * @param parameters List of parameters for the command. For easy of use, + * consider the OdooCommand object or ObjectAdapter * @return The result of the call * @throws XmlRpcException */ @@ -331,16 +416,13 @@ public Object executeCommandWithContext(final String objectName, final String co } /** - * Executes a workflow by sending a signal to the workflow engine for a - * specific object. This functions calls the 'exec_workflow' method on the - * object All parameters are prepended by: "databaseName,userID,password" + * Executes a workflow by sending a signal to the workflow engine for a specific + * object. This functions calls the 'exec_workflow' method on the object All + * parameters are prepended by: "databaseName,userID,password" * - * @param objectName - * Object or model name to send the signal for - * @param signal - * Signal name to send, for example order_confirm - * @param objectID - * Specific object ID to send the signal for + * @param objectName Object or model name to send the signal for + * @param signal Signal name to send, for example order_confirm + * @param objectID Specific object ID to send the signal for * @throws XmlRpcException */ public void executeWorkflow(final String objectName, final String signal, final int objectID) @@ -364,21 +446,22 @@ public Version getServerVersion() throws XmlRpcException { return serverVersion; } - public byte[] executeReportService(String reportName, Object[] ids) throws XmlRpcException { + public byte[] executeReportService(String reportName, String reportMethod, Object[] ids) throws XmlRpcException { byte[] finalResults; + Object[] reportParams = new Object[] { databaseName, userID, password, reportName, ids }; - if (getServerVersion().getMajor() < 11) { OdooXmlRpcProxy client = new OdooXmlRpcProxy(protocol, host, port, RPCServices.RPC_REPORT); - - Map result = (Map)client.execute("render_report", reportParams); - finalResults = DatatypeConverter.parseBase64Binary( - (String)result.get("result")); - + Map result = (Map) client.execute(reportMethod, reportParams); + finalResults = DatatypeConverter.parseBase64Binary((String) result.get("result")); + } else { // Implement changes thanks to // https://github.com/OCA/odoorpc/issues/20 - finalResults = null; + OdooXmlRpcProxy client = new OdooXmlRpcProxy(protocol, host, port, RPCServices.RPC_OBJECT); + ByteBuffer result = (ByteBuffer) this.executeCommandWithContext("ir.actions.report", reportMethod, + reportParams); + finalResults = result.array(); } return finalResults; @@ -401,4 +484,14 @@ public int getUserID() { public Context getContext() { return context; } + + public JsonRpcHttpClient getJsonclient() { + return jsonclient; + } + + public Object[] getLogin_args() { + Object[] args = new Object[] { databaseName, userID, password }; + + return args; + } } diff --git a/src/test/java/com/odoojava/api/ReportAdapterTest.java b/src/test/java/com/odoojava/api/ReportAdapterTest.java index da17fe8..abcf4e0 100644 --- a/src/test/java/com/odoojava/api/ReportAdapterTest.java +++ b/src/test/java/com/odoojava/api/ReportAdapterTest.java @@ -34,17 +34,8 @@ public class ReportAdapterTest { @BeforeClass public static void setUp() throws Exception { - // session = new ReportAdapterTest(RPCProtocol.RPC_HTTPS, - // "demo.odoo.mind-and-go.net", 443, "demov10-ce", "admin", - // "admin-demo"); - // session = new Session(RPCProtocol.RPC_HTTPS, - // "demo.odoo.mind-and-go.net", - // 443, - // "demv10-ce", - // "admin", - // "admin-demo"); DemoDbGetter.getDemoDb(new DemoDbConnectionDataSetter()); - session = new Session(RPCProtocol.RPC_HTTPS, + session = new Session(protocol, host, port, databaseName, userName,password); session.startSession();