001: package dinamica;
002:
003: import java.io.BufferedInputStream;
004: import java.io.ByteArrayOutputStream;
005: import java.net.HttpURLConnection;
006: import java.net.URL;
007: import javax.servlet.http.Cookie;
008:
009: /*
010: import java.security.KeyStore;
011: import java.io.FileInputStream;
012: import javax.net.ssl.HttpsURLConnection;
013: import javax.net.ssl.KeyManagerFactory;
014: import javax.net.ssl.SSLContext;
015: import javax.net.ssl.SSLSocketFactory;
016: import javax.net.ssl.TrustManagerFactory;
017: */
018:
019: /**
020: * Base class to create Output classes. This kind of classes
021: * represent the "view" part of this framework MVC mechanism.
022: * This class consumes recordsets published by transactions, which
023: * have been previously executed by the controller.<br>
024: * <br>
025: * Creation date: 4/10/2003<br>
026: * Last Update: 4/10/2003<br>
027: * (c) 2003 Martin Cordova<br>
028: * This code is released under the LGPL license<br>
029: * @author Martin Cordova
030: */
031: public class GenericOutput extends AbstractModule implements IRowEvent {
032:
033: /* flag used to alternate colors (method onNewRow) */
034: private int rowColor = 0;
035:
036: /**
037: * Generate text-based output using automatic data binding.<br>
038: * This class will consume Recordsets from the "data" object
039: * passed to this method, which is a Transaction that has been
040: * executed before calling the method.<br>
041: * This methods performs the data binding between the templates
042: * and the recordsets, according to the parameters defined in the
043: * config.xml file - it is a kind of VB DataControl, if you will!
044: * All its functionality relies on the TemplateEngine class.
045: * Since this method performs a lot of work, all descendants of this class that
046: * want to reimplement this method, should call super.print(TemplateEngine, Transaction) in the first line
047: * of the reimplemented method in order to reuse all this functionality.
048: * @param te TemplateEngine containing the text based template ready for data binding
049: * @param data Transaction object that publishes one or more recordsets
050: * @throws Throwable
051: */
052: public void print(TemplateEngine te, GenericTransaction data)
053: throws Throwable {
054:
055: /* for each print command */
056: if (data != null) {
057: Recordset rs = _config.getPrintCommands();
058:
059: while (rs.next()) {
060:
061: String nullExpr = null;
062: if (_config.contentType.equals("text/html"))
063: nullExpr = " ";
064: else
065: nullExpr = "";
066:
067: // patch 2004-08-31 - set pagesize via request parameter (optional)
068: String pagesize = getRequest().getParameter("pagesize");
069: if (pagesize != null && pagesize.trim().equals(""))
070: pagesize = null;
071:
072: if (pagesize == null)
073: pagesize = rs.getString("pagesize");
074: // end patch
075:
076: String mode = (String) rs.getValue("mode");
077: String tag = (String) rs.getValue("tag");
078: String control = (String) rs.getValue("control");
079: String rsname = (String) rs.getValue("recordset");
080: String altColors = (String) rs
081: .getValue("alternate-colors");
082: String nullValue = (String) rs.getValue("null-value");
083:
084: if (nullValue != null)
085: nullExpr = nullValue;
086:
087: if (mode.equals("table")) {
088: Recordset x = data.getRecordset(rsname);
089:
090: //patch 2007-06-19 ajax paging support
091: if (x.getPageCount() > 0)
092: pagesize = String.valueOf(x.getPageSize());
093:
094: if (pagesize != null) {
095: int page = 0;
096:
097: // patch 2004-08-31 - mantain original pagesize if available
098: if (x.getPageCount() == 0)
099: x.setPageSize(Integer.parseInt(pagesize));
100: // end patch
101:
102: String pageNumber = getRequest().getParameter(
103: "pagenumber");
104: if (pageNumber == null || pageNumber.equals("")) {
105: page = x.getPageNumber();
106: if (page == 0)
107: page = 1;
108: } else {
109: page = Integer.parseInt(pageNumber);
110: }
111: x = x.getPage(page);
112: }
113:
114: if (altColors != null && altColors.equals("true"))
115: te.setRowEventObject(this );
116:
117: te.replace(x, nullExpr, tag);
118: } else if (mode.equals("form")) {
119: nullExpr = "";
120: Recordset x = data.getRecordset(rsname);
121: if (x.getRecordCount() > 0
122: && x.getRecordNumber() < 0)
123: x.first();
124:
125: //PATCH 2005-03-01 - enhance error message if recordset is empty
126: if (x.getRecordCount() == 0)
127: throw new Throwable(
128: "Recordset ["
129: + rsname
130: + "] has no records; can't print (mode=form) using an empty Recordset.");
131:
132: te.replace(x, nullExpr);
133: } else if (mode.equals("combo")) {
134: if (control == null)
135: throw new Throwable(
136: "'control' attribute cannot be null when print-mode='combo'");
137: Recordset x = data.getRecordset(rsname);
138: if (x.getRecordCount() > 1)
139: te.setComboValue(control, x);
140: else if (x.getRecordCount() > 0
141: && x.getRecordNumber() < 0)
142: x.first();
143: te.setComboValue(control, String.valueOf(x
144: .getValue(control)));
145: } else if (mode.equals("checkbox")) {
146: if (control == null)
147: throw new Throwable(
148: "'control' attribute cannot be null when print-mode='checkbox'");
149: Recordset x = data.getRecordset(rsname);
150: te.setCheckbox(control, x);
151: } else if (mode.equals("radio")) {
152: if (control == null)
153: throw new Throwable(
154: "'control' attribute cannot be null when print-mode='radio'");
155: Recordset x = data.getRecordset(rsname);
156: if (x.getRecordCount() > 0
157: && x.getRecordNumber() < 0)
158: x.first();
159: te.setRadioButton(control, String.valueOf(x
160: .getValue(control)));
161: } else if (mode.equals("clear")) {
162: te.clearFieldMarkers();
163: } else {
164: throw new Throwable("Invalid print mode [" + mode
165: + "] attribute in config.xml: "
166: + _config.path);
167: }
168:
169: }
170:
171: }
172:
173: }
174:
175: /**
176: * This method is called for non text based output (images, binaries, etc.).
177: * Reimplementations of this method MUST write the output
178: * thru the Servlet OutputStream.
179: * @param data Transaction object
180: * @throws Throwable
181: */
182: public void print(GenericTransaction data) throws Throwable {
183: }
184:
185: /**
186: * Implementation of the interface dinamica.IRowEvent.<br>
187: * This code is used to alternate row colors,
188: * the row template must include the special
189: * field marker ${fld:_rowStyle} which will be replaced
190: * by the style parameters set in web.xml.
191: * @see dinamica.IRowEvent#onNewRow(dinamica.Recordset, int, java.lang.String)
192: */
193: public String onNewRow(Recordset rs, String rowTemplate)
194: throws Throwable {
195:
196: /*
197: * This code is used to alternate row colors,
198: * the row template must include the special
199: * field marker ${fld:_rowStyle} which will be replaced
200: * by the style parameters set in web.xml.
201: */
202:
203: String style1 = getContext().getInitParameter("def-color1");
204: String style2 = getContext().getInitParameter("def-color2");
205: String currentStyle = "";
206:
207: if (rowColor == 0) {
208: rowColor = 1;
209: currentStyle = style1;
210: } else {
211: rowColor = 0;
212: currentStyle = style2;
213: }
214:
215: rowTemplate = StringUtil.replace(rowTemplate,
216: "${fld:_rowStyle}", currentStyle);
217:
218: return rowTemplate;
219:
220: }
221:
222: /**
223: * resets value of private variable rowColor. This
224: * variable control alternate printing of colors
225: * for tables (print mode="table")
226: *
227: */
228: protected void resetRowColor() {
229: this .rowColor = 0;
230: }
231:
232: /**
233: * Return byte array with the content of a remote binary resource
234: * accessed via HTTP(S).
235: * @param url URL of the resource
236: * @param sessionID Session ID
237: * @param logStdout If TRUE will print trace log to System.out.
238: * @return Byte array with the content of the file
239: * @throws Throwable In case of any HTTP error of if the data cannot
240: * be read for any reason.
241: */
242: protected byte[] getImage(String url, String sessionID,
243: boolean logStdout) throws Throwable {
244:
245: HttpURLConnection urlc = null;
246: BufferedInputStream bin = null;
247: final int bufferSize = 10240;
248: byte[] buffer = null;
249: URL page = new URL(url);
250: ByteArrayOutputStream bout = new ByteArrayOutputStream();
251:
252: if (logStdout)
253: System.err.println("Waiting for reply...:" + url);
254:
255: try {
256: urlc = (HttpURLConnection) page.openConnection();
257:
258: /*
259: * experimental patch when using SSL & client certificates
260: if (url.startsWith("https")) {
261: KeyStore ks = KeyStore.getInstance("jks");
262: FileInputStream in = new FileInputStream("c:/tomcat6/conf/keystore");
263: ks.load(in, "basica".toCharArray());
264: in.close();
265: KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
266: keyManagerFactory.init(ks, "basica".toCharArray());
267: TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
268: trustManagerFactory.init(ks);
269: SSLContext sslContext = SSLContext.getInstance("TLS");
270: sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
271: SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
272: HttpsURLConnection connection = (HttpsURLConnection) urlc;
273: connection.setSSLSocketFactory(sslSocketFactory);
274: urlc = (HttpURLConnection)connection;
275: }
276: */
277:
278: urlc.setUseCaches(false);
279: urlc.addRequestProperty("Host", getRequest()
280: .getServerName());
281: urlc
282: .addRequestProperty("Cookie", "JSESSIONID="
283: + sessionID);
284: urlc.addRequestProperty("Cache-Control", "max-age=0");
285:
286: if (logStdout) {
287: System.err.println("Content-type = "
288: + urlc.getContentType());
289: System.err.println("Content-length = "
290: + urlc.getContentLength());
291: System.err.println("Response-code = "
292: + urlc.getResponseCode());
293: System.err.println("Response-message = "
294: + urlc.getResponseMessage());
295: }
296:
297: int retCode = urlc.getResponseCode();
298: String retMsg = urlc.getResponseMessage();
299: if (retCode >= 400)
300: throw new Throwable("HTTP Error: " + retCode + " - "
301: + retMsg + " - URL:" + url);
302:
303: int size = urlc.getContentLength();
304: if (size > 0)
305: buffer = new byte[size];
306: else
307: buffer = new byte[bufferSize];
308:
309: bin = new BufferedInputStream(urlc.getInputStream(),
310: buffer.length);
311:
312: int bytesRead = 0;
313: do {
314: bytesRead = bin.read(buffer);
315: if (bytesRead > 0)
316: bout.write(buffer, 0, bytesRead);
317: } while (bytesRead != -1);
318:
319: if (logStdout) {
320: System.err.println("Connection closed.");
321: }
322:
323: return bout.toByteArray();
324:
325: } catch (Throwable e) {
326: throw e;
327: } finally {
328: if (bin != null)
329: bin.close();
330:
331: if (urlc != null)
332: urlc.disconnect();
333: }
334:
335: }
336:
337: /**
338: * Retrieve the session ID from the request headers
339: * looking for a cookie named JSESSIONID. This method was
340: * implemented because some Servers (WepSphere 5.1) won't
341: * return the real cookie value when the HttpSession.getId()
342: * method is invoked, which causes big trouble when retrieving an
343: * image from an Action using HTTP and the session ID. This problem
344: * was discovered while testing PDF reports with charts on WAS 5.1, it
345: * is specific to WAS 5.1, but this method works well with all tested
346: * servlet engines, including Resin 2.x.
347: * @return The session ID as stored in the cookie header, or NULL if it can find the cookie.
348: * @throws Throwable
349: */
350: protected String getSessionID() {
351: String value = null;
352: Cookie c[] = getRequest().getCookies();
353: for (int i = 0; i < c.length; i++) {
354: if (c[i].getName().equals("JSESSIONID")) {
355: value = c[i].getValue();
356: break;
357: }
358: }
359: return value;
360: }
361:
362: /**
363: * Return byte array with the content of a remote binary resource
364: * accessed via HTTP(S) - a Cookie header (JSESSIONID) with the current
365: * session ID will be added to the request headers
366: * @param url URL of the resource
367: * @param logStdout If TRUE will print trace log to System.err.
368: * @return Byte array with the content of the file
369: * @throws Throwable In case of any HTTP error of if the data cannot
370: * be read for any reason.
371: */
372: protected byte[] getImage(String url, boolean logStdout)
373: throws Throwable {
374: String sID = getSessionID();
375: return getImage(url, sID, logStdout);
376: }
377:
378: /**
379: * Returns base URL for retrieving images from the same host
380: * where the application is running. The programmer will need to
381: * add the rest of the path, like /action/chart or /images/logo.png, etc.
382: * @return Base URL to retrieve images from current host
383: */
384: protected String getServerBaseURL() {
385: //URL prefix to retrieve images from same host/context/session
386: String server = "http://";
387: if (getRequest().isSecure())
388: server = "https://";
389: server = server + getRequest().getServerName() + ":"
390: + getRequest().getServerPort()
391: + getRequest().getContextPath();
392: return server;
393: }
394:
395: }
|