]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
0256887ebf24c791e005d63fdfc50448f41c6698
[tmp/jakarta-migration.git] /
1 /**
2  *  This document is a part of the source code and related artifacts
3  *  for CollectionSpace, an open source collections management system
4  *  for museums and related institutions:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
18  *  Unless required by applicable law or agreed to in writing, software
19  *  distributed under the License is distributed on an "AS IS" BASIS,
20  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  *  See the License for the specific language governing permissions and
22  *  limitations under the License.
23  */
24 package org.collectionspace.services.report.nuxeo;
25
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileNotFoundException;
29 import java.io.FileOutputStream;
30 import java.io.InputStream;
31 import java.nio.file.Files;
32 import java.sql.Connection;
33 import java.sql.SQLException;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37
38 import javax.ws.rs.core.MediaType;
39 import javax.naming.NamingException;
40 import javax.ws.rs.core.Response;
41
42 import net.sf.jasperreports.engine.JRException;
43 import net.sf.jasperreports.engine.JRExporter;
44 import net.sf.jasperreports.engine.JRExporterParameter;
45 import net.sf.jasperreports.engine.JRParameter;
46 import net.sf.jasperreports.engine.JasperCompileManager;
47 import net.sf.jasperreports.engine.JasperFillManager;
48 import net.sf.jasperreports.engine.JasperPrint;
49 import net.sf.jasperreports.engine.export.JRCsvExporter;
50 import net.sf.jasperreports.engine.export.JRCsvExporterParameter;
51 import net.sf.jasperreports.engine.export.JRHtmlExporter;
52 import net.sf.jasperreports.engine.export.JRPdfExporter;
53 import net.sf.jasperreports.engine.export.JRXmlExporter;
54 import net.sf.jasperreports.engine.export.ooxml.JRDocxExporter;
55 import net.sf.jasperreports.engine.export.ooxml.JRPptxExporter;
56 import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
57
58 import org.collectionspace.services.ReportJAXBSchema;
59 import org.collectionspace.services.report.ReportsCommon;
60 import org.collectionspace.services.client.PoxPayloadIn;
61 import org.collectionspace.services.client.PoxPayloadOut;
62 import org.collectionspace.services.client.ReportClient;
63 import org.collectionspace.services.common.CSWebApplicationException;
64 import org.collectionspace.services.common.ServiceMain;
65 import org.collectionspace.services.common.api.JEEServerDeployment;
66 import org.collectionspace.services.common.api.FileTools;
67 import org.collectionspace.services.common.api.Tools;
68 import org.collectionspace.services.common.context.ServiceContext;
69 import org.collectionspace.services.common.document.BadRequestException;
70 import org.collectionspace.services.common.document.DocumentException;
71 import org.collectionspace.services.common.document.DocumentWrapper;
72 import org.collectionspace.services.common.invocable.Invocable;
73 import org.collectionspace.services.common.invocable.InvocationContext;
74 import org.collectionspace.services.common.storage.JDBCTools;
75 import org.collectionspace.services.jaxb.InvocableJAXBSchema;
76 import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler;
77 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
78 import org.collectionspace.services.nuxeo.client.java.NuxeoRepositoryClientImpl;
79 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
80 import org.jfree.util.Log;
81 import org.nuxeo.ecm.core.api.DocumentModel;
82 import org.nuxeo.ecm.core.api.model.PropertyException;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
85
86 /**
87  * ReportDocumentModelHandler
88  *
89  * $LastChangedRevision: $
90  * $LastChangedDate: $
91  */
92 public class ReportDocumentModelHandler extends NuxeoDocumentModelHandler<ReportsCommon> {
93     private final Logger logger = LoggerFactory.getLogger(ReportDocumentModelHandler.class);
94     private static String REPORTS_FOLDER = "reports";
95     private static String CSID_LIST_SEPARATOR = ",";
96     
97     private static String REPORTS_STD_CSID_PARAM = "csid";
98     private static String REPORTS_STD_GROUPCSID_PARAM = "groupcsid";
99     private static String REPORTS_STD_CSIDLIST_PARAM = "csidlist";
100     private static String REPORTS_STD_TENANTID_PARAM = "tenantid";
101     
102         public InputStream invokeReport(
103                         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
104                         String csid,
105                         InvocationContext invContext,
106                         StringBuffer outMimeType,
107                         StringBuffer outReportFileName) throws Exception {
108                 CoreSessionInterface repoSession = null;
109                 boolean releaseRepoSession = false;
110
111                 String invocationMode = invContext.getMode();
112                 String modeProperty = null;
113                 HashMap<String, Object> params = new HashMap<String, Object>();
114                 params.put(REPORTS_STD_TENANTID_PARAM, ctx.getTenantId());
115                 boolean checkDocType = true;
116                 
117                 // Note we set before we put in the default ones, so they cannot override tenant or CSID.
118                 setParamsFromContext(params, invContext);
119                 
120                 if(Invocable.INVOCATION_MODE_SINGLE.equalsIgnoreCase(invocationMode)) {
121                         modeProperty = InvocableJAXBSchema.SUPPORTS_SINGLE_DOC;
122                 params.put(REPORTS_STD_CSID_PARAM, invContext.getSingleCSID());
123                 } else if(Invocable.INVOCATION_MODE_LIST.equalsIgnoreCase(invocationMode)) {
124                         modeProperty = InvocableJAXBSchema.SUPPORTS_DOC_LIST;
125                         List<String> csids = null;
126                         InvocationContext.ListCSIDs listThing = invContext.getListCSIDs();
127                                 if(listThing!=null) {
128                                         csids = listThing.getCsid();
129                                 }
130                                 if(csids==null||csids.isEmpty()){
131                                 throw new BadRequestException(
132                                                 "ReportResource: Report invoked in list mode, with no csids in list." );
133                                 }
134                                 StringBuilder sb = new StringBuilder();
135                                 boolean first = true;
136                                 for(String csidItem : csids) {
137                                         if(first)
138                                                 first = false;
139                                         else
140                                                 sb.append(CSID_LIST_SEPARATOR);
141                                         sb.append(csidItem);
142                                 }
143                 params.put(REPORTS_STD_CSIDLIST_PARAM, sb.toString());
144                 } else if(Invocable.INVOCATION_MODE_GROUP.equalsIgnoreCase(invocationMode)) {
145                         modeProperty = InvocableJAXBSchema.SUPPORTS_GROUP;
146                 params.put(REPORTS_STD_GROUPCSID_PARAM, invContext.getGroupCSID());
147                 } else if(Invocable.INVOCATION_MODE_NO_CONTEXT.equalsIgnoreCase(invocationMode)) {
148                         modeProperty = InvocableJAXBSchema.SUPPORTS_NO_CONTEXT;
149                         checkDocType = false;
150                 } else {
151                         throw new BadRequestException("ReportResource: unknown Invocation Mode: "
152                                 +invocationMode);
153                 }
154                 
155                 NuxeoRepositoryClientImpl repoClient = (NuxeoRepositoryClientImpl)this.getRepositoryClient(ctx);
156                 repoSession = this.getRepositorySession();
157                 if (repoSession == null) {
158                         repoSession = repoClient.getRepositorySession(ctx);
159                         releaseRepoSession = true;
160                 }
161
162                 // Get properties from the batch docModel, and release the session
163                 String reportFileNameProperty;
164                 try {
165                         DocumentWrapper<DocumentModel> wrapper = repoClient.getDoc(repoSession, ctx, csid);
166                         DocumentModel docModel = wrapper.getWrappedObject();
167                         Boolean supports = (Boolean) NuxeoUtils.getProperyValue(docModel, modeProperty); //docModel.getPropertyValue(modeProperty);
168                         if(supports == null || !supports) {
169                                 throw new BadRequestException(
170                                                 "ReportResource: This Report does not support Invocation Mode: "
171                                         +invocationMode);
172                         }
173                 if (checkDocType) {
174                         List<String> forDocTypeList = 
175                                 (List<String>) NuxeoUtils.getProperyValue(docModel, InvocableJAXBSchema.FOR_DOC_TYPES); //docModel.getPropertyValue(InvocableJAXBSchema.FOR_DOC_TYPES);
176                         if (forDocTypeList==null || !forDocTypeList.contains(invContext.getDocType())) {
177                                 throw new BadRequestException(
178                                                 "ReportResource: Invoked with unsupported document type: "
179                                                 +invContext.getDocType());
180                         }
181                 }
182                 reportFileNameProperty = (String) NuxeoUtils.getProperyValue(docModel, ReportJAXBSchema.FILENAME); //docModel.getPropertyValue(ReportJAXBSchema.FILENAME)); // Set the outgoing param with the report file name
183                         String reportOutputMime = (String) NuxeoUtils.getProperyValue(docModel, ReportJAXBSchema.OUTPUT_MIME); //docModel.getPropertyValue(ReportJAXBSchema.OUTPUT_MIME);
184                         if(!Tools.isEmpty(reportOutputMime)) {
185                                 outMimeType.append(reportOutputMime);
186                         } else {
187                                 outMimeType.append(ReportClient.DEFAULT_REPORT_OUTPUT_MIME);
188                         }
189                 } catch (PropertyException pe) {
190                         if (logger.isDebugEnabled()) {
191                                 logger.debug("Property exception getting batch values: ", pe);
192                         }
193                         throw pe;
194                 } catch (DocumentException de) {
195                         if (logger.isDebugEnabled()) {
196                                 logger.debug("Problem getting batch doc: ", de);
197                         }
198                         throw de;
199                 } catch (Exception e) {
200                         if (logger.isDebugEnabled()) {
201                                 logger.debug("Caught exception ", e);
202                         }
203                         throw new DocumentException(e);
204                 } finally {
205                         if (releaseRepoSession && repoSession != null) {
206                                 repoClient.releaseRepositorySession(ctx, repoSession);
207                         }
208                 }
209                 
210         return buildReportResult(csid, params, reportFileNameProperty, outMimeType.toString(), outReportFileName);
211         }
212         
213         private void setParamsFromContext(Map<String, Object> params, InvocationContext invContext) {
214                 InvocationContext.Params icParams = invContext.getParams();
215                 if(icParams!= null) {
216                         List<InvocationContext.Params.Param> icParamList = icParams.getParam();
217                         if(icParamList != null) {
218                                 for(InvocationContext.Params.Param param:icParamList) {
219                                         String key = param.getKey();
220                                         String value = param.getValue();
221                                         if(!Tools.isEmpty(key) && !Tools.isEmpty(value)) {
222                                                 params.put(key, value);
223                                         }
224                                 }
225                         }
226                 }
227                 
228         }
229
230     private InputStream buildReportResult(String reportCSID, 
231                 HashMap<String, Object> params, String reportFileName, String outputMimeType, StringBuffer outReportFileName)
232                                 throws Exception {
233                 Connection conn = null;
234                 InputStream result = null;
235                 
236         try {
237                 String fileNameBase = Tools.getFilenameBase(reportFileName);
238                 String compiledReportFilename = fileNameBase+ReportClient.COMPILED_REPORT_EXTENSION;
239                 String reportDescriptionFilename = fileNameBase+ReportClient.REPORT_DECSRIPTION_EXTENSION;
240                 
241                         String basePath = ServiceMain.getInstance().getServerRootDir() +
242                                                                 File.separator + JEEServerDeployment.CSPACE_DIR_NAME + 
243                                                                 File.separator + REPORTS_FOLDER +
244                                                                 // File.separator + tenantName +
245                                                                 File.separator; // + reportFileName;
246                         
247                         String compiledFilePath = basePath+compiledReportFilename; 
248                         File f = new File(compiledFilePath);
249                         if(!f.exists()) { // Need to compile the file
250                                 // First verify that there is a source file.
251                                 String sourceFilePath = basePath+reportDescriptionFilename; 
252                                 File f2 = new File(sourceFilePath);
253                                 if(!f2.exists()) { // Missing source file - error!
254                                         logger.error("Report for csid={} is missing the specified source file: {}",
255                                                                         reportCSID, sourceFilePath);
256                                         throw new RuntimeException("Report is missing the specified source file!");
257                                 }
258                 logger.info("Report for csid={} is not compiled. Compiling first, and saving to: {}",
259                                 reportCSID, compiledFilePath);
260                 JasperCompileManager.compileReportToFile(sourceFilePath, compiledFilePath);
261                         }                               
262                                 
263                         conn = getConnection();
264         
265             if (logger.isTraceEnabled()) {
266                 logger.trace("ReportResource for csid=" + reportCSID
267                                 +" output as "+outputMimeType+" using report file: "+compiledFilePath);
268             }
269                         FileInputStream fileStream = new FileInputStream(compiledFilePath);
270         
271                         // export report to pdf and build a response with the bytes
272                         //JasperExportManager.exportReportToPdf(jasperprint);
273                         
274                         JRExporter exporter = null;
275                         // Strip extension from report filename.
276                         String outputFilename = reportFileName;
277                         // Strip extension from report filename.
278                         int idx = outputFilename.lastIndexOf("."); 
279                         if(idx>0)
280                                 outputFilename = outputFilename.substring(0, idx);
281                         // Strip any sub-dir from report filename.
282                         idx = outputFilename.lastIndexOf(File.separator); 
283                         if(idx>0)
284                                 outputFilename = outputFilename.substring(idx+1);
285                         if(outputMimeType.equals(MediaType.APPLICATION_XML)) {
286                                 params.put(JRParameter.IS_IGNORE_PAGINATION, Boolean.TRUE);
287                                 exporter = new JRXmlExporter();
288                                 outputFilename = outputFilename+".xml";
289                         } else if(outputMimeType.equals(MediaType.TEXT_HTML)) {
290                                 exporter = new JRHtmlExporter();
291                                 outputFilename = outputFilename+".html";
292                         } else if(outputMimeType.equals(ReportClient.PDF_MIME_TYPE)) {
293                                 exporter = new JRPdfExporter();
294                                 outputFilename = outputFilename+".pdf";
295                         } else if(outputMimeType.equals(ReportClient.CSV_MIME_TYPE)) {
296                                 params.put(JRParameter.IS_IGNORE_PAGINATION, Boolean.TRUE);
297                                 exporter = new JRCsvExporter();
298                                 exporter.setParameter(JRCsvExporterParameter.FIELD_DELIMITER, ",");
299                                 outputFilename = outputFilename+".csv";
300                         } else if(outputMimeType.equals(ReportClient.TSV_MIME_TYPE)) {
301                                 params.put(JRParameter.IS_IGNORE_PAGINATION, Boolean.TRUE);
302                                 exporter = new JRCsvExporter();
303                                 exporter.setParameter(JRCsvExporterParameter.FIELD_DELIMITER, "\t");
304                                 outputFilename = outputFilename+".csv";
305                         } else if(outputMimeType.equals(ReportClient.MSWORD_MIME_TYPE)  // Understand msword as docx
306                                         || outputMimeType.equals(ReportClient.OPEN_DOCX_MIME_TYPE)) {
307                                 exporter = new JRDocxExporter();
308                                 outputFilename = outputFilename+".docx";
309                         } else if(outputMimeType.equals(ReportClient.MSEXCEL_MIME_TYPE) // Understand msexcel as xlsx
310                                         || outputMimeType.equals(ReportClient.OPEN_XLSX_MIME_TYPE)) {
311                                 exporter = new JRXlsxExporter();
312                                 outputFilename = outputFilename+".xlsx";
313                         } else if(outputMimeType.equals(ReportClient.MSPPT_MIME_TYPE)   // Understand msppt as xlsx
314                                         || outputMimeType.equals(ReportClient.OPEN_PPTX_MIME_TYPE)) {
315                                 exporter = new JRPptxExporter();
316                                 outputFilename = outputFilename+".pptx";
317                         } else {
318                                 logger.error("Reporting: unsupported output MIME type - defaulting to PDF");
319                                 exporter = new JRPdfExporter();
320                                 outputFilename = outputFilename+"-default-to.pdf";
321                         }
322                         outReportFileName.append(outputFilename); // Set the out going param to the report's final file name
323                         // FIXME: Logging temporarily set to INFO level for CSPACE-5766;
324                         // can change to TRACE or DEBUG level as warranted thereafter
325                         if (logger.isInfoEnabled()) {
326                             logger.info(FileTools.getJavaTmpDirInfo());
327                         }
328                         // fill the report
329                         JasperPrint jasperPrint = JasperFillManager.fillReport(fileStream, params,conn);
330                                 
331                         // Report will be to a temporary file.
332                         File tempOutputFile = Files.createTempFile("report-", null).toFile();
333                         FileOutputStream tempOutputStream = new FileOutputStream(tempOutputFile);                       
334                         exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
335                         exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, tempOutputStream);
336                         exporter.exportReport();
337                         tempOutputStream.close();
338                         
339                         result = new FileInputStream(tempOutputFile);
340                 return result;
341         } catch (SQLException sqle) {
342             // SQLExceptions can be chained. We have at least one exception, so
343             // set up a loop to make sure we let the user know about all of them
344             // if there happens to be more than one.
345             if (logger.isDebugEnabled()) {
346                     SQLException tempException = sqle;
347                     while (null != tempException) {
348                                 logger.debug("SQL Exception: " + sqle.getLocalizedMessage());
349         
350                         // loop to the next exception
351                         tempException = tempException.getNextException();
352                     }
353             }
354             Response response = Response.status(
355                     Response.Status.INTERNAL_SERVER_ERROR).entity(
356                                 "Invoke failed (SQL problem) on Report csid=" + reportCSID).type("text/plain").build();
357             throw new CSWebApplicationException(sqle, response);
358         } catch (JRException jre) {
359             if (logger.isDebugEnabled()) {
360                 logger.debug("JR Exception: " + jre.getLocalizedMessage() + " Cause: "+jre.getCause());
361             }
362             Response response = Response.status(
363                     Response.Status.INTERNAL_SERVER_ERROR).entity(
364                                 "Invoke failed (Jasper problem) on Report csid=" + reportCSID).type("text/plain").build();
365             throw new CSWebApplicationException(jre, response);
366         } catch (FileNotFoundException fnfe) {
367             if (logger.isDebugEnabled()) {
368                 logger.debug("FileNotFoundException: " + fnfe.getLocalizedMessage());
369             }
370             Response response = Response.status(
371                     Response.Status.INTERNAL_SERVER_ERROR).entity(
372                                 "Invoke failed (SQL problem) on Report csid=" + reportCSID).type("text/plain").build();
373             throw new CSWebApplicationException(fnfe, response);
374                 } finally {
375                 if (conn!=null) {
376                         try {
377                                 conn.close();
378                 } catch (SQLException sqle) {
379                     // SQLExceptions can be chained. We have at least one exception, so
380                     // set up a loop to make sure we let the user know about all of them
381                     // if there happens to be more than one.
382                     if (logger.isDebugEnabled()) {
383                                 logger.debug("SQL Exception closing connection: " 
384                                                 + sqle.getLocalizedMessage());
385                     }
386                 } catch (Exception e) {
387                     if (logger.isDebugEnabled()) {
388                         logger.debug("Exception closing connection", e);
389                     }
390                 }
391                 }
392         }
393     }
394
395     private Connection getConnection() throws NamingException, SQLException {
396         Connection result = null;
397         
398         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = this.getServiceContext();
399         try {
400                 String repositoryName = ctx.getRepositoryName();
401                 if (repositoryName != null && repositoryName.trim().isEmpty() == false) {
402                         String cspaceInstanceId = ServiceMain.getInstance().getCspaceInstanceId();
403                         String databaseName = JDBCTools.getDatabaseName(repositoryName, cspaceInstanceId);
404                         result = JDBCTools.getConnection(JDBCTools.NUXEO_READER_DATASOURCE_NAME, databaseName);
405                 }
406                 } catch (Exception e) {
407                         Log.error(e);
408                         throw new NamingException();
409                 }
410         
411         return result;
412     }
413
414 }
415