001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui.apps.logfile;
029
030import org.opencms.main.CmsLog;
031import org.opencms.ui.CmsVaadinUtils;
032import org.opencms.ui.components.CmsBasicDialog;
033import org.opencms.util.CmsStringUtil;
034
035import java.io.File;
036import java.io.FileInputStream;
037import java.io.FileNotFoundException;
038import java.io.FileOutputStream;
039import java.io.IOException;
040import java.io.InputStream;
041import java.io.OutputStream;
042import java.util.ArrayList;
043import java.util.HashSet;
044import java.util.List;
045import java.util.Set;
046import java.util.zip.ZipEntry;
047import java.util.zip.ZipOutputStream;
048
049import org.apache.commons.logging.Log;
050
051import com.vaadin.server.FileDownloader;
052import com.vaadin.server.Resource;
053import com.vaadin.server.StreamResource;
054import com.vaadin.ui.Button;
055import com.vaadin.ui.Button.ClickEvent;
056import com.vaadin.ui.Button.ClickListener;
057import com.vaadin.ui.Window;
058import com.vaadin.v7.data.Property.ValueChangeEvent;
059import com.vaadin.v7.data.Property.ValueChangeListener;
060import com.vaadin.v7.shared.ui.combobox.FilteringMode;
061import com.vaadin.v7.ui.CheckBox;
062import com.vaadin.v7.ui.ComboBox;
063
064/**
065 * Class for the Download dialog.<p>
066 */
067public class CmsLogDownloadDialog extends CmsBasicDialog {
068
069    /**
070     * Helper class for generating the zip file for the log download.<p>
071     */
072    class ZipGenerator {
073
074        /** The set of generated parent directories. */
075        private Set<String> m_directories = new HashSet<>();
076
077        /** The zip output. */
078        private ZipOutputStream m_zos;
079
080        /**
081         * Creates a new instance.<p>
082         *
083         * @param output the output stream to write to
084         */
085        public ZipGenerator(OutputStream output) {
086
087            m_zos = new ZipOutputStream(output);
088        }
089
090        /**
091         * Adds a file to a zip-stream.<p>
092         *
093         * @param directory to be zipped
094         * @param file to be added zo zip
095         * @throws IOException exception
096         */
097        public void addToZip(File directory, File file) throws IOException {
098
099            FileInputStream fis = new FileInputStream(file);
100            String dirPath = directory.getCanonicalPath();
101            String filePath = file.getCanonicalPath();
102            String zipFilePath;
103            if (CmsStringUtil.isPrefixPath(dirPath, filePath)) {
104                zipFilePath = filePath.substring(dirPath.length() + 1, filePath.length());
105            } else {
106                String parentName = generateParentName(file);
107                if (!m_directories.contains(parentName)) {
108                    m_zos.putNextEntry(new ZipEntry(parentName));
109                }
110                m_directories.add(parentName);
111                zipFilePath = CmsStringUtil.joinPaths(parentName, file.getName());
112            }
113            ZipEntry zipEntry = new ZipEntry(zipFilePath);
114            m_zos.putNextEntry(zipEntry);
115
116            byte[] bytes = new byte[1024];
117            int length;
118            while ((length = fis.read(bytes)) >= 0) {
119                m_zos.write(bytes, 0, length);
120            }
121            m_zos.closeEntry();
122            fis.close();
123        }
124
125        /**
126         * Closes the zip stream.<p>
127         *
128         * @throws IOException if something goes wrong
129         */
130        public void close() throws IOException {
131
132            m_zos.close();
133        }
134
135        /**
136         * Generates the name of the parent directory in the zip file for a non-standard log file location.<p>
137         *
138         * @param file the log file
139         * @return the generated parent directory name
140         */
141        String generateParentName(File file) {
142
143            // we might be on Windows, so we can't just assume path is /foo/bar/baz
144            List<String> pathComponents = new ArrayList<>();
145            for (int i = 0; i < (file.toPath().getNameCount() - 1); i++) {
146                pathComponents.add(file.toPath().getName(i).toString());
147            }
148            // need trailing slash when writing directory entries to ZipOutputStream
149            return CmsStringUtil.listAsString(pathComponents, "_") + "/";
150        }
151
152    }
153
154    /** Logger.*/
155    private static Log LOG = CmsLog.getLog(CmsLogDownloadDialog.class.getName());
156
157    /**vaadin serial id.*/
158    private static final long serialVersionUID = -7447640082260176245L;
159
160    /** Path to zip file.*/
161    private static final String ZIP_PATH = CmsLogFileApp.LOG_FOLDER + "logs.zip";
162
163    /**File downloader. */
164    protected FileDownloader m_downloader;
165
166    /**Vaadin component.*/
167    private Button m_cancel;
168
169    /**Vaadin component.**/
170    private CheckBox m_donwloadAll;
171
172    /**Vaadin component.*/
173    private ComboBox m_file;
174
175    /**Vaadin component.*/
176    private Button m_ok;
177
178    /**total size of log files in MB.*/
179    private double m_totalSize;
180
181    /**
182     * public constructor.<p>
183     *
184     * @param window to hold the dialog
185     * @param filePath path of currently shown file
186     */
187    public CmsLogDownloadDialog(final Window window, String filePath) {
188
189        CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), null);
190
191        m_totalSize = 0;
192        for (File file : CmsLogFileOptionProvider.getLogFiles()) {
193            if (!file.getAbsolutePath().endsWith(".zip")) {
194                m_file.addItem(file.getAbsolutePath());
195                m_totalSize += file.length() / (1024 * 1024);
196            }
197        }
198
199        m_donwloadAll.setVisible(m_totalSize < 150);
200
201        m_file.setFilteringMode(FilteringMode.CONTAINS);
202        m_file.setNullSelectionAllowed(false);
203        m_file.setNewItemsAllowed(false);
204
205        m_file.select(filePath);
206
207        m_cancel.addClickListener(new ClickListener() {
208
209            private static final long serialVersionUID = 4336654148546091114L;
210
211            public void buttonClick(ClickEvent event) {
212
213                window.close();
214
215            }
216
217        });
218        m_downloader = new FileDownloader(getDownloadResource());
219        m_downloader.extend(m_ok);
220
221        ValueChangeListener listener = new ValueChangeListener() {
222
223            private static final long serialVersionUID = -1127012396158402096L;
224
225            public void valueChange(ValueChangeEvent event) {
226
227                m_downloader.setFileDownloadResource(getDownloadResource());
228                setComboBoxEnable();
229
230            }
231        };
232
233        m_file.addValueChangeListener(listener);
234        m_donwloadAll.addValueChangeListener(listener);
235
236    }
237
238    /**
239     * Creates log-file resource for download.<p>
240     *
241     * @return vaadin resource
242     */
243    protected Resource getDownloadResource() {
244
245        String pathToDownload = (String)m_file.getValue();
246
247        if (m_donwloadAll.getValue().booleanValue()) {
248            pathToDownload = ZIP_PATH;
249            writeZipFile();
250        }
251
252        final File downloadFile = new File(pathToDownload);
253
254        return new StreamResource(new StreamResource.StreamSource() {
255
256            private static final long serialVersionUID = -8868657402793427460L;
257
258            public InputStream getStream() {
259
260                try {
261                    return new FileInputStream(downloadFile);
262                } catch (FileNotFoundException e) {
263                    return null;
264                }
265
266            }
267        }, downloadFile.getName());
268    }
269
270    /**
271     * En/ disables the combo box for files.<p>
272     */
273    protected void setComboBoxEnable() {
274
275        m_file.setEnabled(!m_donwloadAll.getValue().booleanValue());
276    }
277
278    /**
279     * Writes the zip file with all logs.<p>
280     */
281    private void writeZipFile() {
282
283        try {
284            FileOutputStream fos = new FileOutputStream(ZIP_PATH);
285            ZipGenerator zipGen = new ZipGenerator(fos);
286            for (File file : CmsLogFileOptionProvider.getLogFiles()) {
287                if (!file.isDirectory() & !ZIP_PATH.equals(file.getAbsolutePath())) {
288                    zipGen.addToZip(new File(CmsLogFileApp.LOG_FOLDER), file);
289                }
290            }
291            zipGen.close();
292            fos.close();
293
294        } catch (IOException e) {
295            LOG.error("unable to build zip file", e);
296        }
297    }
298}