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