| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.felix.webconsole.internal.misc; |
| |
| |
| import java.io.*; |
| import java.lang.reflect.Method; |
| import java.net.URL; |
| import java.text.*; |
| import java.util.*; |
| import java.util.zip.*; |
| |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.felix.webconsole.*; |
| import org.apache.felix.webconsole.internal.OsgiManagerPlugin; |
| import org.apache.felix.webconsole.internal.i18n.ResourceBundleManager; |
| import org.osgi.framework.*; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| |
| /** |
| * ConfigurationRender plugin renders the configuration status - a textual |
| * representation of the current framework status. The content itself is |
| * internally generated by the {@link ConfigurationPrinter} plugins. |
| */ |
| public class ConfigurationRender extends SimpleWebConsolePlugin implements OsgiManagerPlugin |
| { |
| |
| private static final String LABEL = "config"; |
| private static final String TITLE = "%configStatus.pluginTitle"; |
| private static final String[] CSS_REFS = { "/res/ui/configurationrender.css" }; |
| |
| // use English as the locale for all non-display titles |
| private static final Locale DEFAULT = Locale.ENGLISH; |
| |
| /** |
| * Formatter pattern to generate a relative path for the generation |
| * of the plain text or zip file representation of the status. The file |
| * name consists of a base name and the current time of status generation. |
| */ |
| private static final SimpleDateFormat FILE_NAME_FORMAT = new SimpleDateFormat( "'" + LABEL |
| + "/configuration-status-'yyyyMMdd'-'HHmmZ" ); |
| |
| /** |
| * Formatter pattern to render the current time of status generation. |
| */ |
| private static final DateFormat DISPLAY_DATE_FORMAT = DateFormat.getDateTimeInstance( DateFormat.LONG, |
| DateFormat.LONG, Locale.US ); |
| |
| /** |
| * The resource bundle manager to allow for status printer title |
| * localization |
| */ |
| private final ResourceBundleManager resourceBundleManager; |
| |
| private ServiceTracker cfgPrinterTracker; |
| |
| private int cfgPrinterTrackerCount; |
| |
| private ArrayList configurationPrinters; |
| |
| /** Default constructor */ |
| public ConfigurationRender( final ResourceBundleManager resourceBundleManager ) |
| { |
| super( LABEL, TITLE, CSS_REFS ); |
| this.resourceBundleManager = resourceBundleManager; |
| } |
| |
| |
| /** |
| * @see org.apache.felix.webconsole.SimpleWebConsolePlugin#deactivate() |
| */ |
| public void deactivate() |
| { |
| // make sure the service tracker is closed and removed on deactivate |
| ServiceTracker oldTracker = cfgPrinterTracker; |
| if ( oldTracker != null ) |
| { |
| oldTracker.close(); |
| } |
| cfgPrinterTracker = null; |
| configurationPrinters = null; |
| |
| super.deactivate(); |
| } |
| |
| /** |
| * Returns the requested printer name if the current request contains one |
| */ |
| private String getRequestedPrinterName(final HttpServletRequest request) |
| { |
| String name = request.getPathInfo(); |
| final int dotPos = name.lastIndexOf('.'); |
| if ( dotPos != -1 ) |
| { |
| name = name.substring(0, dotPos); |
| } |
| name = name.substring( name.lastIndexOf('/') + 1); |
| name = WebConsoleUtil.urlDecode( name ); |
| return name; |
| } |
| /** |
| * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) |
| */ |
| protected final void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, |
| IOException |
| { |
| if ( request.getPathInfo().endsWith( ".txt" ) ) |
| { |
| response.setContentType( "text/plain; charset=utf-8" ); |
| ConfigurationWriter pw = new PlainTextConfigurationWriter( response.getWriter() ); |
| printConfigurationStatus( pw, ConfigurationPrinter.MODE_TXT, getRequestedPrinterName(request) ); |
| pw.flush(); |
| } |
| else if ( request.getPathInfo().endsWith( ".zip" ) ) |
| { |
| String type = getServletContext().getMimeType( request.getPathInfo() ); |
| if ( type == null ) |
| { |
| type = "application/x-zip"; |
| } |
| response.setContentType( type ); |
| |
| ZipOutputStream zip = new ZipOutputStream( response.getOutputStream() ); |
| zip.setLevel( Deflater.BEST_SPEED ); |
| zip.setMethod( ZipOutputStream.DEFLATED ); |
| |
| final ConfigurationWriter pw = new ZipConfigurationWriter( zip ); |
| printConfigurationStatus( pw, ConfigurationPrinter.MODE_ZIP, getRequestedPrinterName(request) ); |
| pw.flush(); |
| |
| addAttachments( pw, ConfigurationPrinter.MODE_ZIP ); |
| zip.finish(); |
| } |
| else if ( request.getPathInfo().endsWith( ".nfo" ) ) |
| { |
| WebConsoleUtil.setNoCache( response ); |
| response.setContentType( "text/html; charset=utf-8" ); |
| |
| final String name = getRequestedPrinterName(request); |
| |
| ConfigurationWriter pw = new HtmlConfigurationWriter( response.getWriter() ); |
| pw.println ( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" ); |
| pw.println ( " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" ); |
| pw.println ( "<html xmlns=\"http://www.w3.org/1999/xhtml\">" ); |
| pw.println ( "<head><title>dummy</title></head><body><div>" ); |
| |
| Collection printers = getPrintersForLabel(name); |
| if ( printers != null ) |
| { |
| for (Iterator i = printers.iterator(); i.hasNext();) |
| { |
| final PrinterDesc desc = (PrinterDesc) i.next(); |
| printConfigurationPrinter( pw, desc, ConfigurationPrinter.MODE_WEB ); |
| pw.println( "</div></body></html>" ); |
| return; |
| } |
| } |
| |
| response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid configuration printer: " + name); |
| } |
| else |
| { |
| super.doGet( request, response ); |
| } |
| } |
| |
| |
| /** |
| * @see org.apache.felix.webconsole.AbstractWebConsolePlugin#renderContent(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) |
| */ |
| protected final void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException |
| { |
| |
| //ConfigurationWriter pw = new HtmlConfigurationWriter( response.getWriter() ); |
| PrintWriter pw = response.getWriter(); |
| pw.println( "<script type='text/javascript' src='${appRoot}/res/ui/ui.tabs.paging.js'></script>" ); |
| pw.println( "<script type='text/javascript' src='${appRoot}/res/ui/configurationrender.js'></script>" ); |
| |
| pw.println( "<br/><p class=\"statline\">"); |
| |
| final Date currentTime = new Date(); |
| synchronized ( DISPLAY_DATE_FORMAT ) |
| { |
| pw.print("Date: "); |
| pw.println(DISPLAY_DATE_FORMAT.format(currentTime)); |
| } |
| |
| synchronized ( FILE_NAME_FORMAT ) |
| { |
| String fileName = FILE_NAME_FORMAT.format( currentTime ); |
| pw.print("<br/>Download as <a href='"); |
| pw.print(fileName); |
| pw.print(".txt'>[Single File]</a> or as <a href='"); |
| pw.print(fileName); |
| pw.println(".zip'>[ZIP]</a>"); |
| } |
| |
| pw.println("</p>"); // status line |
| |
| // display some information while the data is loading |
| // load the data (hidden to begin with) |
| pw.println("<div id='tabs'> <!-- tabs container -->"); |
| pw.println("<ul> <!-- tabs on top -->"); |
| |
| // print headers only |
| final String pluginRoot = request.getAttribute( WebConsoleConstants.ATTR_PLUGIN_ROOT ) + "/"; |
| Collection printers = getConfigurationPrinters(); |
| for (Iterator i = printers.iterator(); i.hasNext();) |
| { |
| final PrinterDesc desc = (PrinterDesc) i.next(); |
| final String label = desc.label; |
| final String title = desc.title; |
| pw.print("<li><a href='" + pluginRoot + label + ".nfo'>" + title + "</a></li>" ); |
| } |
| pw.println("</ul> <!-- end tabs on top -->"); |
| pw.println(); |
| |
| pw.println("</div> <!-- end tabs container -->"); |
| |
| pw.println("<div id=\"waitDlg\" title=\"${configStatus.wait}\" class=\"ui-helper-hidden\"><img src=\"${appRoot}/res/imgs/loading.gif\" alt=\"${configStatus.wait}\" />${configStatus.wait.msg}</div>"); |
| |
| pw.flush(); |
| } |
| |
| private List getPrintersForLabel(final String label) |
| { |
| List list = null; |
| for ( Iterator cpi = getConfigurationPrinters().iterator(); cpi.hasNext(); ) |
| { |
| final PrinterDesc desc = (PrinterDesc) cpi.next(); |
| if (desc.label.equals( label ) ) |
| { |
| if ( list == null ) |
| { |
| list = new ArrayList(); |
| list.add(desc); |
| } |
| } |
| } |
| return list; |
| } |
| |
| private void printConfigurationStatus( ConfigurationWriter pw, final String mode, final String optionalLabel ) |
| { |
| // check if we have printers for that label |
| Collection printers = getPrintersForLabel(optionalLabel); |
| if ( printers == null ) |
| { |
| // if not use all |
| printers = getConfigurationPrinters(); |
| } |
| |
| for ( Iterator cpi = printers.iterator(); cpi.hasNext(); ) |
| { |
| final PrinterDesc desc = (PrinterDesc) cpi.next(); |
| if ( desc.match(mode) ) |
| { |
| printConfigurationPrinter( pw, desc, mode ); |
| } |
| } |
| } |
| |
| |
| private final synchronized List getConfigurationPrinters() |
| { |
| if ( cfgPrinterTracker == null ) |
| { |
| try |
| { |
| cfgPrinterTracker = new ServiceTracker( getBundleContext(), |
| getBundleContext().createFilter("(|(" + Constants.OBJECTCLASS + "=" + ConfigurationPrinter.class.getName() + ")" + |
| "(&(" + WebConsoleConstants.PLUGIN_LABEL + "=*)(&(" |
| + WebConsoleConstants.PLUGIN_TITLE + "=*)(" |
| + WebConsoleConstants.CONFIG_PRINTER_MODES + "=*))))"), |
| null ); |
| } |
| catch (InvalidSyntaxException e) |
| { |
| // ignore |
| } |
| cfgPrinterTracker.open(); |
| cfgPrinterTrackerCount = -1; |
| } |
| |
| if ( cfgPrinterTrackerCount != cfgPrinterTracker.getTrackingCount() ) |
| { |
| SortedMap cp = new TreeMap(); |
| ServiceReference[] refs = cfgPrinterTracker.getServiceReferences(); |
| if ( refs != null ) |
| { |
| for ( int i = 0; i < refs.length; i++ ) |
| { |
| final ServiceReference ref = refs[i]; |
| final Object service = cfgPrinterTracker.getService( ref ); |
| if ( service != null ) |
| { |
| if ( service instanceof ConfigurationPrinter ) |
| { |
| final ConfigurationPrinter cfgPrinter = (ConfigurationPrinter) service; |
| addConfigurationPrinter( cp, cfgPrinter, refs[i].getBundle(), |
| ref.getProperty( WebConsoleConstants.PLUGIN_LABEL ), |
| ref.getProperty( ConfigurationPrinter.PROPERTY_MODES ) ); |
| } |
| else |
| { |
| ConfigurationPrinter cfgPrinter = null; |
| // first: printConfiguration(PrintWriter, String) |
| try |
| { |
| final Method method = service.getClass().getDeclaredMethod("printConfiguration", |
| new Class[] {PrintWriter.class, String.class}); |
| cfgPrinter = new ModeAwareConfigurationPrinterAdapter(service, |
| (String)ref.getProperty( WebConsoleConstants.PLUGIN_TITLE ), method); |
| } |
| catch (NoSuchMethodException nsme) |
| { |
| // ignore |
| } |
| if ( cfgPrinter == null ) |
| { |
| // second: printConfiguration(PrintWriter) |
| try |
| { |
| final Method method = service.getClass().getDeclaredMethod("printConfiguration", |
| new Class[] {PrintWriter.class}); |
| cfgPrinter = new ConfigurationPrinterAdapter(service, |
| (String)ref.getProperty( WebConsoleConstants.PLUGIN_TITLE ), method); |
| } |
| catch (NoSuchMethodException nsme) |
| { |
| // ignore |
| } |
| } |
| |
| if ( cfgPrinter != null ) |
| { |
| addConfigurationPrinter( cp, cfgPrinter, ref.getBundle(), |
| ref.getProperty( WebConsoleConstants.PLUGIN_LABEL ), |
| ref.getProperty( WebConsoleConstants.CONFIG_PRINTER_MODES ) ); |
| } |
| } |
| } |
| } |
| } |
| configurationPrinters = new ArrayList(cp.values()); |
| cfgPrinterTrackerCount = cfgPrinterTracker.getTrackingCount(); |
| } |
| |
| return configurationPrinters; |
| } |
| |
| |
| private final void addConfigurationPrinter( final SortedMap printers, |
| final ConfigurationPrinter cfgPrinter, |
| final Bundle provider, |
| final Object labelProperty, |
| final Object mode ) |
| { |
| final String title = getTitle( cfgPrinter.getTitle(), provider ); |
| String sortKey = title; |
| if ( printers.containsKey( sortKey ) ) |
| { |
| int idx = -1; |
| String idxTitle; |
| do |
| { |
| idx++; |
| idxTitle = sortKey + idx; |
| } |
| while ( printers.containsKey( idxTitle ) ); |
| sortKey = idxTitle; |
| } |
| String label = ( labelProperty instanceof String ) ? ( String ) labelProperty : sortKey; |
| printers.put( sortKey, new PrinterDesc( cfgPrinter, title, label, mode ) ); |
| } |
| |
| |
| // This is Sling stuff, we comment it out for now |
| // private void printRawFrameworkProperties(PrintWriter pw) { |
| // pw.println("*** Raw Framework properties:"); |
| // |
| // File file = new File(getBundleContext().getProperty("sling.home"), |
| // "sling.properties"); |
| // if (file.exists()) { |
| // Properties props = new Properties(); |
| // InputStream ins = null; |
| // try { |
| // ins = new FileInputStream(file); |
| // props.load(ins); |
| // } catch (IOException ioe) { |
| // // handle or ignore |
| // } finally { |
| // IOUtils.closeQuietly(ins); |
| // } |
| // |
| // SortedSet keys = new TreeSet(props.keySet()); |
| // for (Iterator ki = keys.iterator(); ki.hasNext();) { |
| // Object key = ki.next(); |
| // infoLine(pw, null, (String) key, props.get(key)); |
| // } |
| // |
| // } else { |
| // pw.println(" No Framework properties in " + file); |
| // } |
| // |
| // pw.println(); |
| // } |
| |
| |
| private final void printConfigurationPrinter( final ConfigurationWriter pw, final PrinterDesc desc, |
| final String mode ) |
| { |
| pw.title( desc.title ); |
| final ConfigurationPrinter cp = desc.printer; |
| try |
| { |
| if ( cp instanceof ModeAwareConfigurationPrinter ) |
| { |
| ( ( ModeAwareConfigurationPrinter ) cp ).printConfiguration( pw, mode ); |
| } |
| else |
| { |
| cp.printConfiguration( pw ); |
| } |
| } |
| catch ( Throwable t ) |
| { |
| pw.println(); |
| pw.println( "Configuration Printer failed: " + t.toString() ); |
| pw.println(); |
| log( "Configuration Printer " + desc.title + " (" + cp.getClass() + ") failed", t ); |
| } |
| pw.end(); |
| } |
| |
| |
| /** |
| * Renders an info line - element in the framework configuration. The info line will |
| * look like: |
| * <pre> |
| * label = value |
| * </pre> |
| * |
| * Optionally it can be indented by a specific string. |
| * |
| * @param pw the writer to print to |
| * @param indent indentation string |
| * @param label the label data |
| * @param value the data itself. |
| */ |
| public static final void infoLine( PrintWriter pw, String indent, String label, Object value ) |
| { |
| if ( indent != null ) |
| { |
| pw.print( indent ); |
| } |
| |
| if ( label != null ) |
| { |
| pw.print( label ); |
| pw.print( " = " ); |
| } |
| |
| pw.print( WebConsoleUtil.toString( value ) ); |
| |
| pw.println(); |
| } |
| |
| private final String getTitle( final String title, final Bundle provider ) |
| { |
| if ( !title.startsWith( "%" ) ) |
| { |
| return title; |
| } |
| |
| ResourceBundle res = resourceBundleManager.getResourceBundle( provider, DEFAULT ); |
| return res.getString( title.substring( 1 ) ); |
| } |
| |
| private abstract static class ConfigurationWriter extends PrintWriter |
| { |
| |
| ConfigurationWriter( Writer delegatee ) |
| { |
| super( delegatee ); |
| } |
| |
| |
| abstract void title( String title ); |
| |
| |
| abstract void end(); |
| |
| |
| public void handleAttachments( final String title, final URL[] urls ) throws IOException |
| { |
| throw new UnsupportedOperationException( "handleAttachments not supported by this configuration writer: " |
| + this ); |
| } |
| |
| } |
| |
| private static class HtmlConfigurationWriter extends ConfigurationWriter |
| { |
| |
| // whether or not to filter "<" signs in the output |
| private boolean doFilter; |
| |
| |
| HtmlConfigurationWriter( Writer delegatee ) |
| { |
| super( delegatee ); |
| } |
| |
| |
| public void title( String title ) |
| { |
| doFilter = true; |
| } |
| |
| |
| public void end() |
| { |
| doFilter = false; |
| } |
| |
| |
| // IE has an issue with white-space:pre in our case so, we write |
| // <br/> instead of [CR]LF to get the line break. This also works |
| // in other browsers. |
| public void println() |
| { |
| if ( doFilter ) |
| { |
| oldch = '_'; |
| this.write('\n'); // write <br/> |
| } |
| else |
| { |
| super.println(); |
| } |
| oldch = '\n'; |
| } |
| |
| private int oldch = '_'; |
| |
| // write the character unmodified unless filtering is enabled and |
| // the character is a "<" in which case < is written |
| public void write(final int character) |
| { |
| if (doFilter) |
| { |
| switch (character) |
| { |
| case '<': |
| super.write('&'); |
| super.write('l'); |
| super.write('t'); |
| super.write(';'); |
| break; |
| case '>': |
| super.write('&'); |
| super.write('g'); |
| super.write('t'); |
| super.write(';'); |
| break; |
| case '&': |
| super.write('&'); |
| super.write('a'); |
| super.write('m'); |
| super.write('p'); |
| super.write(';'); |
| break; |
| case ' ': |
| super.write('&'); |
| super.write('n'); |
| super.write('b'); |
| super.write('s'); |
| super.write('p'); |
| super.write(';'); |
| break; |
| case '\r': |
| case '\n': |
| if (oldch != '\r' && oldch != '\n') |
| {// don't add twice <br> |
| super.write('<'); |
| super.write('b'); |
| super.write('r'); |
| super.write('/'); |
| super.write('>'); |
| super.write('\n'); |
| } |
| break; |
| default: |
| super.write(character); |
| } |
| } |
| else |
| { |
| super.write(character); |
| } |
| oldch = character; |
| } |
| |
| |
| // write the characters unmodified unless filtering is enabled in |
| // which case the writeFiltered(String) method is called for filtering |
| public void write( final char[] chars, final int off, final int len ) |
| { |
| if ( doFilter ) |
| { |
| for (int i = off; i < len; i++) |
| { |
| this.write(chars[i]); |
| } |
| } |
| else |
| { |
| super.write( chars, off, len ); |
| } |
| } |
| |
| |
| // write the string unmodified unless filtering is enabled in |
| // which case the writeFiltered(String) method is called for filtering |
| public void write( final String string, final int off, final int len ) |
| { |
| write(string.toCharArray(), off, len); |
| } |
| |
| } |
| |
| private void addAttachments( final ConfigurationWriter cf, final String mode ) |
| throws IOException |
| { |
| for ( Iterator cpi = getConfigurationPrinters().iterator(); cpi.hasNext(); ) |
| { |
| // check if printer supports zip mode |
| final PrinterDesc desc = (PrinterDesc) cpi.next(); |
| if ( desc.match(mode) ) |
| { |
| // check if printer implements binary configuration printer |
| if ( desc.printer instanceof AttachmentProvider ) |
| { |
| final URL[] attachments = ((AttachmentProvider)desc.printer).getAttachments(mode); |
| if ( attachments != null ) |
| { |
| cf.handleAttachments( desc.title, attachments ); |
| } |
| } |
| } |
| } |
| |
| } |
| |
| private static final class PrinterDesc |
| { |
| public final ConfigurationPrinter printer; |
| public final String title; |
| public final String label; |
| private final String[] modes; |
| |
| private static final List CUSTOM_MODES = new ArrayList(); |
| static |
| { |
| CUSTOM_MODES.add( ConfigurationPrinter.MODE_TXT ); |
| CUSTOM_MODES.add( ConfigurationPrinter.MODE_WEB ); |
| CUSTOM_MODES.add( ConfigurationPrinter.MODE_ZIP ); |
| } |
| |
| |
| public PrinterDesc( final ConfigurationPrinter printer, final String title, final String label, |
| final Object modes ) |
| { |
| this.printer = printer; |
| this.title = title; |
| this.label = label; |
| if ( modes == null || !( modes instanceof String || modes instanceof String[] ) ) |
| { |
| this.modes = null; |
| } |
| else |
| { |
| if ( modes instanceof String ) |
| { |
| if ( CUSTOM_MODES.contains(modes) ) |
| { |
| this.modes = new String[] {modes.toString()}; |
| } |
| else |
| { |
| this.modes = null; |
| } |
| } |
| else |
| { |
| final String[] values = (String[])modes; |
| boolean valid = values.length > 0; |
| for(int i=0; i<values.length; i++) |
| { |
| if ( !CUSTOM_MODES.contains(values[i]) ) |
| { |
| valid = false; |
| break; |
| } |
| } |
| if ( valid) |
| { |
| this.modes = values; |
| } |
| else |
| { |
| this.modes = null; |
| } |
| } |
| } |
| } |
| |
| public boolean match(final String mode) |
| { |
| if ( this.modes == null) |
| { |
| return true; |
| } |
| for(int i=0; i<this.modes.length; i++) |
| { |
| if ( this.modes[i].equals(mode) ) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| private static class PlainTextConfigurationWriter extends ConfigurationWriter |
| { |
| |
| PlainTextConfigurationWriter( Writer delegatee ) |
| { |
| super( delegatee ); |
| } |
| |
| |
| public void title( String title ) |
| { |
| print( "*** " ); |
| print( title ); |
| println( ":" ); |
| } |
| |
| |
| public void end() |
| { |
| println(); |
| } |
| } |
| |
| private static class ZipConfigurationWriter extends ConfigurationWriter |
| { |
| private final ZipOutputStream zip; |
| |
| private int counter; |
| |
| |
| ZipConfigurationWriter( ZipOutputStream zip ) |
| { |
| super( new OutputStreamWriter( zip ) ); |
| this.zip = zip; |
| } |
| |
| |
| public void title( String title ) |
| { |
| String name = MessageFormat.format( "{0,number,000}-{1}.txt", new Object[] |
| { new Integer( counter ), title } ); |
| |
| counter++; |
| |
| ZipEntry entry = new ZipEntry( name ); |
| try |
| { |
| zip.putNextEntry( entry ); |
| } |
| catch ( IOException ioe ) |
| { |
| // should handle |
| } |
| } |
| |
| private OutputStream startFile( String title, String name) |
| { |
| final String path = MessageFormat.format( "{0,number,000}-{1}/{2}", new Object[] |
| { new Integer( counter ), title, name } ); |
| ZipEntry entry = new ZipEntry( path ); |
| try |
| { |
| zip.putNextEntry( entry ); |
| } |
| catch ( IOException ioe ) |
| { |
| // should handle |
| } |
| return zip; |
| } |
| |
| public void handleAttachments( final String title, final URL[] attachments) |
| throws IOException |
| { |
| for(int i = 0; i < attachments.length; i++) |
| { |
| final URL current = attachments[i]; |
| final String path = current.getPath(); |
| final String name; |
| if ( path == null || path.length() == 0 ) |
| { |
| // sanity code, we should have a path, but if not let's |
| // just create some random name |
| name = "file" + Double.doubleToLongBits( Math.random() ); |
| } |
| else |
| { |
| final int pos = path.lastIndexOf('/'); |
| name = (pos == -1 ? path : path.substring(pos + 1)); |
| } |
| final OutputStream os = this.startFile(title, name); |
| final InputStream is = current.openStream(); |
| try |
| { |
| IOUtils.copy(is, os); |
| } |
| finally |
| { |
| IOUtils.closeQuietly(is); |
| } |
| this.end(); |
| } |
| |
| // increase the filename counter |
| counter++; |
| } |
| |
| |
| public void end() |
| { |
| flush(); |
| |
| try |
| { |
| zip.closeEntry(); |
| } |
| catch ( IOException ioe ) |
| { |
| // should handle |
| } |
| } |
| } |
| } |