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