View Javadoc

1   /*******************************************************************************
2    *  Imixs Workflow 
3    *  Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,  
4    *  http://www.imixs.com
5    *  
6    *  This program is free software; you can redistribute it and/or 
7    *  modify it under the terms of the GNU General Public License 
8    *  as published by the Free Software Foundation; either version 2 
9    *  of the License, or (at your option) any later version.
10   *  
11   *  This program is distributed in the hope that it will be useful, 
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
14   *  General Public License for more details.
15   *  
16   *  You can receive a copy of the GNU General Public
17   *  License at http://www.gnu.org/licenses/gpl.html
18   *  
19   *  Project: 
20   *  	http://www.imixs.org
21   *  	http://java.net/projects/imixs-workflow
22   *  
23   *  Contributors:  
24   *  	Imixs Software Solutions GmbH - initial API and implementation
25   *  	Ralph Soika - Software Developer
26   *******************************************************************************/
27  
28  package org.imixs.workflow.jaxrs;
29  
30  import java.io.ByteArrayInputStream;
31  import java.io.ByteArrayOutputStream;
32  import java.io.IOException;
33  import java.io.InputStreamReader;
34  import java.io.OutputStream;
35  import java.io.StringWriter;
36  import java.util.Collection;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.Set;
40  import java.util.Vector;
41  
42  import javax.ejb.EJB;
43  import javax.ejb.Stateless;
44  import javax.servlet.http.HttpServletRequest;
45  import javax.ws.rs.Consumes;
46  import javax.ws.rs.DELETE;
47  import javax.ws.rs.DefaultValue;
48  import javax.ws.rs.GET;
49  import javax.ws.rs.POST;
50  import javax.ws.rs.PUT;
51  import javax.ws.rs.Path;
52  import javax.ws.rs.PathParam;
53  import javax.ws.rs.Produces;
54  import javax.ws.rs.QueryParam;
55  import javax.ws.rs.WebApplicationException;
56  import javax.ws.rs.core.Context;
57  import javax.ws.rs.core.MultivaluedMap;
58  import javax.ws.rs.core.Response;
59  import javax.ws.rs.core.StreamingOutput;
60  import javax.ws.rs.core.UriInfo;
61  import javax.xml.bind.JAXBContext;
62  import javax.xml.bind.Marshaller;
63  import javax.xml.transform.Result;
64  import javax.xml.transform.Source;
65  import javax.xml.transform.Transformer;
66  import javax.xml.transform.TransformerFactory;
67  import javax.xml.transform.sax.SAXResult;
68  import javax.xml.transform.stream.StreamResult;
69  import javax.xml.transform.stream.StreamSource;
70  
71  import org.apache.fop.apps.FOUserAgent;
72  import org.apache.fop.apps.Fop;
73  import org.apache.fop.apps.FopFactory;
74  import org.apache.fop.apps.MimeConstants;
75  import org.imixs.workflow.ItemCollection;
76  import org.imixs.workflow.xml.EntityCollection;
77  import org.imixs.workflow.xml.EntityTable;
78  import org.imixs.workflow.xml.XMLItemCollection;
79  import org.imixs.workflow.xml.XMLItemCollectionAdapter;
80  
81  /**
82   * The WorkflowService Handler supports methods to process different kind of
83   * request URIs
84   * 
85   * @author rsoika
86   * 
87   */
88  @Path("/report")
89  @Produces({ "text/html", "application/xml", "application/json" })
90  @Stateless
91  public class ReportRestService {
92  
93  	@EJB
94  	org.imixs.workflow.jee.ejb.EntityService entityService;
95  
96  	@EJB
97  	org.imixs.workflow.jee.ejb.ReportService reportService;
98  
99  	@javax.ws.rs.core.Context
100 	private static HttpServletRequest servletRequest;
101 
102 	@GET
103 	@Produces("text/html")
104 	@Path("/")
105 	public StreamingOutput getHelpHTML() {
106 
107 		return new StreamingOutput() {
108 			public void write(OutputStream out) throws IOException,
109 					WebApplicationException {
110 
111 				out.write("<html><head>".getBytes());
112 				out.write("<style>".getBytes());
113 				out.write("table {padding:0px;width: 100%;margin-left: -2px;margin-right: -2px;}"
114 						.getBytes());
115 				out.write("body,td,select,input,li {font-family: Verdana, Helvetica, Arial, sans-serif;font-size: 13px;}"
116 						.getBytes());
117 				out.write("table th {color: white;background-color: #bbb;text-align: left;font-weight: bold;}"
118 						.getBytes());
119 
120 				out.write("table th,table td {font-size: 12px;}".getBytes());
121 
122 				out.write("table tr.a {background-color: #ddd;}".getBytes());
123 
124 				out.write("table tr.b {background-color: #eee;}".getBytes());
125 
126 				out.write("</style>".getBytes());
127 				out.write("</head><body>".getBytes());
128 
129 				// body
130 				out.write("<h1>Imixs Workflow REST Service</h1>".getBytes());
131 				out.write("<p>Read the Imixs REST Service <a href=\"http://doc.imixs.org/xml/restservice.html\">Online Help</a> for a detailed description of this Service.</p>"
132 						.getBytes());
133 				out.write("<p>See the <a href=\"http://www.imixs.org\">Imixs Workflow Project Site</a> for general informations.</p>"
134 						.getBytes());
135 
136 				// end
137 				out.write("</body></html>".getBytes());
138 			}
139 		};
140 
141 	}
142 
143 	@GET
144 	@Path("/reports")
145 	public EntityCollection getAllReports(
146 			@DefaultValue("0") @QueryParam("start") int start,
147 			@DefaultValue("10") @QueryParam("count") int count) {
148 		try {
149 			Collection<ItemCollection> col = null;
150 			try {
151 				col = reportService.getReportList(start, count);
152 				return XMLItemCollectionAdapter.putCollection(col);
153 			} catch (Exception e) {
154 				e.printStackTrace();
155 			}
156 			return new EntityCollection();
157 
158 		} catch (Exception e) {
159 			e.printStackTrace();
160 		}
161 		return new EntityCollection();
162 	}
163 
164 	/**
165 	 * Returns a single report by name or by uniqueid
166 	 * 
167 	 * @param name
168 	 *            reportname or uniqueid of report
169 	 * @return
170 	 */
171 	@GET
172 	@Path("/reports/{name}")
173 	public XMLItemCollection getReport(@PathParam("name") String name) {
174 		try {
175 			ItemCollection itemCol = reportService.getReport(name);
176 			return XMLItemCollectionAdapter.putItemCollection(itemCol);
177 		} catch (Exception e) {
178 			e.printStackTrace();
179 		}
180 		return null;
181 	}
182 
183 	/**
184 	 * Executes a single report defined by name or uniqueid
185 	 * 
186 	 * The output depends on the requested media format
187 	 * 
188 	 * Since 2.1.2: the ReportService also supports FOP Transformation. If the
189 	 * ContentType is 'application/pdf' the method will call fopTransofrmation
190 	 * instat of xslTransformation. The FOP API need to be provided by the main
191 	 * application.
192 	 * 
193 	 * @param name
194 	 *            reportname of the report to be executed
195 	 * @return a collection of entiteis
196 	 * 
197 	 */
198 	@GET
199 	@Path("/{name}.ixr")
200 	public Response getExcecuteReport(@PathParam("name") String name,
201 			@DefaultValue("0") @QueryParam("start") int start,
202 			@DefaultValue("10") @QueryParam("count") int count,
203 			@DefaultValue("") @QueryParam("encoding") String encoding,
204 			@Context UriInfo uriInfo) {
205 		Collection<ItemCollection> col = null;
206 		String reportName = null;
207 		String sEQL;
208 		String sXSL;
209 		String sContentType;
210 
211 		try {
212 
213 			reportName = name + ".ixr";
214 
215 			ItemCollection itemCol = reportService.getReport(reportName);
216 			List<String> vAttributList = itemCol
217 					.getItemValue("txtAttributeList");
218 
219 			// get Query and output format
220 			sEQL = itemCol.getItemValueString("txtquery");
221 			sEQL = computeEQLParams(sEQL, uriInfo);
222 			sXSL = itemCol.getItemValueString("txtXSL").trim();
223 			sContentType = itemCol.getItemValueString("txtcontenttype");
224 			if ("".equals(sContentType))
225 				sContentType = "text/html";
226 
227 			// if no encoding is provided by the query string than the encoding
228 			// from the report will be taken
229 			if ("".equals(encoding))
230 				encoding = itemCol.getItemValueString("txtencoding");
231 			// no encoding defined so take a default encoding
232 			// (UTF-8)
233 			if ("".equals(encoding))
234 				encoding = "UTF-8";
235 
236 			// query result
237 			col = entityService.findAllEntities(sEQL, start, count);
238 
239 			// if no XSL is provided return standard html format...?
240 			if ("".equals(sXSL)) {
241 				Response.ResponseBuilder builder = Response.ok(
242 						XMLItemCollectionAdapter.putCollection(col,
243 								vAttributList), "text/html");
244 				return builder.build();
245 			}
246 
247 			// Transform XML per XSL and generate output
248 			EntityCollection xmlCol = XMLItemCollectionAdapter.putCollection(
249 					col, vAttributList);
250 
251 			StringWriter writer = new StringWriter();
252 
253 			JAXBContext context = JAXBContext
254 					.newInstance(EntityCollection.class);
255 
256 			Marshaller m = context.createMarshaller();
257 			m.setProperty("jaxb.encoding", encoding);
258 			m.marshal(xmlCol, writer);
259 
260 			// create a ByteArray Output Stream
261 			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
262 			try {
263 				// test if FOP Tranformation
264 				if ("application/pdf".equals(sContentType.toLowerCase()))
265 					ReportRestService.fopTranformation(writer.toString(), sXSL,
266 							encoding, outputStream);
267 				else
268 					ReportRestService.xslTranformation(writer.toString(), sXSL,
269 							encoding, outputStream);
270 			} finally {
271 				outputStream.close();
272 			}
273 
274 			/*
275 			 * outputStream.toByteArray() did not work here because the encoding
276 			 * will not be considered. For that reason we use the
277 			 * toString(encoding) methdo here
278 			 */
279 			// Response.ResponseBuilder builder = Response.ok(
280 			// outputStream.toByteArray(), sContentType);
281 			Response.ResponseBuilder builder = Response.ok(
282 					outputStream.toString(encoding), sContentType);
283 			return builder.build();
284 		} catch (Exception e) {
285 			e.printStackTrace();
286 		}
287 		return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
288 
289 	}
290 
291 	/**
292 	 * helper method for .pdf file extention
293 	 * 
294 	 * @param name
295 	 *            reportname of the report to be executed
296 	 * @return a collection of entiteis
297 	 * 
298 	 */
299 	@GET
300 	@Path("/{name}.pdf")
301 	public Response getPdfReport(@PathParam("name") String name,
302 			@DefaultValue("0") @QueryParam("start") int start,
303 			@DefaultValue("10") @QueryParam("count") int count,
304 			@DefaultValue("") @QueryParam("encoding") String encoding,
305 			@Context UriInfo uriInfo) {
306 		return this.getExcecuteReport(name, start, count, encoding, uriInfo);
307 	}
308 
309 	/**
310 	 * Returns an HTML Stream with a HTML Datatable corresponding to the report
311 	 * query.
312 	 * 
313 	 * @param name
314 	 * @param start
315 	 * @param count
316 	 * @return
317 	 */
318 	@GET
319 	@Produces("text/html")
320 	@Path("/{name}.html")
321 	public EntityTable getExcecuteReportHTML(@PathParam("name") String name,
322 			@DefaultValue("0") @QueryParam("start") int start,
323 			@DefaultValue("10") @QueryParam("count") int count,
324 			@Context UriInfo uriInfo) {
325 		Collection<ItemCollection> col = null;
326 		String reportName = null;
327 
328 		try {
329 			reportName = name + ".ixr";
330 			ItemCollection itemCol = reportService.getReport(reportName);
331 			String sEQL = itemCol.getItemValueString("txtquery");
332 			sEQL = computeEQLParams(sEQL, uriInfo);
333 
334 			Vector<String> vAttributList = itemCol
335 					.getItemValue("txtAttributeList");
336 			col = entityService.findAllEntities(sEQL, start, count);
337 
338 			EntityCollection entityCol = XMLItemCollectionAdapter
339 					.putCollection(col);
340 			EntityTable entityTable = new EntityTable();
341 			entityTable.setAttributeList(vAttributList);
342 			entityTable.setEntity(entityCol.getEntity());
343 
344 			return entityTable;
345 		} catch (Exception e) {
346 			e.printStackTrace();
347 		}
348 		return null;
349 	}
350 
351 	/**
352 	 * Returns a xml stream from a report execution
353 	 * 
354 	 * If a attribute list is defined in the report only the corresponding
355 	 * properties will be returend in the xml stream.
356 	 * 
357 	 * 
358 	 * @param name
359 	 *            reportname of the report to be executed
360 	 * @param start
361 	 * @param count
362 	 * @return
363 	 * @throws Exception
364 	 */
365 	@GET
366 	@Produces("application/xml")
367 	@Path("/{name}.xml")
368 	public EntityCollection getExcecuteReportXML(
369 			@PathParam("name") String name,
370 			@DefaultValue("0") @QueryParam("start") int start,
371 			@DefaultValue("10") @QueryParam("count") int count,
372 			@Context UriInfo uriInfo) throws Exception {
373 		String reportName = null;
374 		Collection<ItemCollection> col = null;
375 
376 		try {
377 
378 			reportName = name + ".ixr";
379 
380 			ItemCollection itemCol = reportService.getReport(reportName);
381 			List<String> vAttributList = itemCol
382 					.getItemValue("txtAttributeList");
383 
384 			String sEQL = itemCol.getItemValueString("txtquery");
385 			sEQL = computeEQLParams(sEQL, uriInfo);
386 			col = entityService.findAllEntities(sEQL, start, count);
387 
388 			return XMLItemCollectionAdapter.putCollection(col, vAttributList);
389 
390 		} catch (Exception e) {
391 			e.printStackTrace();
392 		}
393 		return null;
394 	}
395 
396 	/**
397 	 * Returns a JSON stream from a report execution
398 	 * 
399 	 * If a attribute list is defined in the report only the corresponding
400 	 * properties will be returend in the xml stream.
401 	 * 
402 	 * 
403 	 * @param name
404 	 *            reportname of the report to be executed
405 	 * @param start
406 	 * @param count
407 	 * @return
408 	 * @throws Exception
409 	 */
410 	@GET
411 	@Produces("application/json")
412 	@Path("/{name}.json")
413 	public EntityCollection getExcecuteReportJSON(
414 			@PathParam("name") String name,
415 			@DefaultValue("0") @QueryParam("start") int start,
416 			@DefaultValue("10") @QueryParam("count") int count,
417 			@Context UriInfo uriInfo) throws Exception {
418 
419 		return getExcecuteReportXML(name, start, count, uriInfo);
420 
421 	}
422 
423 	/**
424 	 * Deletes a report by name or by uniqueid
425 	 * 
426 	 * @param name
427 	 *            of report or uniqueid
428 	 */
429 	@DELETE
430 	@Path("/reports/{name}")
431 	public void deleteModel(@PathParam("version") String name) {
432 		try {
433 			ItemCollection itemCol = reportService.getReport(name);
434 			entityService.remove(itemCol);
435 		} catch (Exception e) {
436 			e.printStackTrace();
437 		}
438 
439 	}
440 
441 	/**
442 	 * This method updates or creates a Report object provided in a
443 	 * XMLItemCollection object
444 	 * 
445 	 * 
446 	 * @param reportCol
447 	 *            - report data
448 	 */
449 	@PUT
450 	@Consumes({ "application/xml", "text/xml" })
451 	public void putReport(XMLItemCollection reportCol) {
452 		ItemCollection itemCollection;
453 		try {
454 			itemCollection = XMLItemCollectionAdapter
455 					.getItemCollection(reportCol);
456 			reportService.updateReport(itemCollection);
457 		} catch (Exception e) {
458 			e.printStackTrace();
459 		}
460 	}
461 
462 	@POST
463 	@Consumes({ "application/xml", "text/xml" })
464 	public void postReport(XMLItemCollection reportCol) {
465 		putReport(reportCol);
466 	}
467 
468 	/**
469 	 * This method transforms an xml string with a provided xsl String
470 	 * 
471 	 * The XMLSource will be read from a InputStreamReader object to provide the
472 	 * XSLTransformer with a inputsream in expected encoding.
473 	 * 
474 	 * 
475 	 * 
476 	 * @param xmlSource
477 	 * @param xsltSourceFile
478 	 * @return
479 	 */
480 
481 	public static void xslTranformation(String xmlSource, String xslSource,
482 			String aEncoding, OutputStream output) throws Exception {
483 		try {
484 			TransformerFactory transFact = TransformerFactory.newInstance();
485 
486 			// generate XML InputStream Reader with encoding
487 			ByteArrayInputStream baisXML = new ByteArrayInputStream(
488 					xmlSource.getBytes());
489 			InputStreamReader isreaderXML;
490 
491 			isreaderXML = new InputStreamReader(baisXML, aEncoding);
492 
493 			Source xmlSrc = new StreamSource(isreaderXML);
494 
495 			// generate XSL InputStream Reader with encoding
496 			ByteArrayInputStream baisXSL = new ByteArrayInputStream(
497 					xslSource.getBytes());
498 			InputStreamReader isreaderXSL = new InputStreamReader(baisXSL,
499 					aEncoding);
500 			Source xslSrc = new StreamSource(isreaderXSL);
501 
502 			Transformer trans = transFact.newTransformer(xslSrc);
503 			trans.transform(xmlSrc, new StreamResult(output));
504 
505 		} finally {
506 
507 		}
508 
509 	}
510 
511 	/**
512 	 * This method dos a apache FOP transformation using the FopFactory
513 	 * 
514 	 * @param xmlSource
515 	 * @param xslSource
516 	 * @param aEncoding
517 	 * @param outputWriter
518 	 */
519 	public static void fopTranformation(String xmlSource, String xslSource,
520 			String aEncoding, OutputStream output) throws Exception {
521 		// configure fopFactory as desired
522 		FopFactory fopFactory = FopFactory.newInstance();
523 
524 		FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
525 		foUserAgent.setBaseURL(fopFactory.getBaseURL());
526 
527 		// configure foUserAgent as desired
528 		// OutputStream out =null;
529 		try {
530 			// Construct fop with desired output format
531 			Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent,
532 					output);
533 
534 			// Setup XSLT
535 			TransformerFactory factory = TransformerFactory.newInstance();
536 			ByteArrayInputStream baisXSL = new ByteArrayInputStream(
537 					xslSource.getBytes());
538 			InputStreamReader isreaderXSL = new InputStreamReader(baisXSL,
539 					aEncoding);
540 			Source xslSrc = new StreamSource(isreaderXSL);
541 
542 			Transformer transformer = factory.newTransformer(xslSrc);
543 
544 			// Setup input for XSLT transformation
545 			ByteArrayInputStream baisXML = new ByteArrayInputStream(
546 					xmlSource.getBytes());
547 			InputStreamReader isreaderXML;
548 
549 			isreaderXML = new InputStreamReader(baisXML, aEncoding);
550 
551 			Source xmlSrc = new StreamSource(isreaderXML);
552 
553 			// Resulting SAX events (the generated FO) must be piped through to
554 			// FOP
555 			Result res = new SAXResult(fop.getDefaultHandler());
556 
557 			// Start XSLT transformation and FOP processing
558 			transformer.transform(xmlSrc, res);
559 
560 			// return res.toString();
561 		} finally {
562 
563 			// out.close();
564 			// output.flush();
565 			// output.close();
566 		}
567 
568 	}
569 
570 	/**
571 	 * This method parses the query Params of a Request URL and adds params to a
572 	 * given EQL Query.
573 	 * 
574 	 * 
575 	 * @param uriInfo
576 	 * @return
577 	 */
578 	private String computeEQLParams(String aQuery, UriInfo uriInfo) {
579 		// test each given QueryParam if it is contained in the EQL Query...
580 		MultivaluedMap<String, String> mvm = uriInfo.getQueryParameters();
581 
582 		Set<String> keys = mvm.keySet();
583 		Iterator<String> iter = keys.iterator();
584 		while (iter.hasNext()) {
585 			// read key
586 			String sKeyName = iter.next().toString();
587 			// test if key is contained in query
588 			if (aQuery.indexOf("?" + sKeyName) > -1) {
589 				String sParamValue = mvm.getFirst(sKeyName);
590 				aQuery = aQuery.replace("?" + sKeyName, sParamValue);
591 			}
592 		}
593 		return aQuery;
594 	}
595 
596 }