/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.designer.extensions.pentaho.repository.util;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.regex.Pattern;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.commons.vfs.FileSystemOptions;
import org.apache.commons.vfs.VFS;
import org.apache.commons.vfs.auth.StaticUserAuthenticator;
import org.pentaho.reporting.designer.core.ReportDesignerBoot;
import org.pentaho.reporting.designer.core.ReportDesignerContext;
import org.pentaho.reporting.designer.core.actions.global.OpenReportAction;
import org.pentaho.reporting.designer.core.auth.AuthenticationData;
import org.pentaho.reporting.designer.core.editor.ReportRenderContext;
import org.pentaho.reporting.designer.core.settings.WorkspaceSettings;
import org.pentaho.reporting.designer.core.util.ExternalToolLauncher;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.modules.parser.bundle.writer.BundleWriter;
import org.pentaho.reporting.engine.classic.core.modules.parser.bundle.writer.BundleWriterException;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.IOUtils;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.base.util.URLEncoder;
import org.pentaho.reporting.libraries.pensol.PentahoSolutionsFileSystemConfigBuilder;
import org.pentaho.reporting.libraries.pensol.PublishRestUtil;
import org.pentaho.reporting.libraries.repository.ContentIOException;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.pentaho.reporting.libraries.xmlns.common.ParserUtil;
public class PublishUtil
{
private static final String WEB_SOLUTION_PREFIX = "web-solution:";
private static final String JCR_SOLUTION_PREFIX = "jcr-solution:";
public static final String SERVER_VERSION = "server-version";
public static final int SERVER_VERSION_SUGAR = 5;
public static final int SERVER_VERSION_LEGACY = 4;
private static final int HTTP_RESPONSE_FAIL = 504; //RepresentS an unknown rest failure as this code
private static final int HTTP_RESPONSE_OK = 200;
private static final String TIMEOUT = "timeout";
protected static String reservedChars = "/\\\t\r\n";
protected static String reservedCharsDisplay = "/, \\, TAB, CR, LF";
private static Pattern containsReservedCharsPattern = makePattern(reservedChars);
private PublishUtil()
{
}
public static ReportRenderContext openReport(final ReportDesignerContext context,
final AuthenticationData loginData,
final String path)
throws IOException, ReportDataFactoryException, ResourceException
{
if (StringUtils.isEmpty(path))
{
throw new IOException("Path is empty.");
}
final String urlPath = path.replaceAll("%", "%25").replaceAll("%2B", "+").replaceAll("\\!", "%21").replaceAll(":", "%3A");
final FileObject connection = createVFSConnection(loginData);
final FileObject object = connection.resolveFile(urlPath);
if (object.exists() == false)
{
throw new FileNotFoundException(path);
}
final InputStream inputStream = object.getContent().getInputStream();
try
{
final ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(8192, (int) object.getContent().getSize()));
IOUtils.getInstance().copyStreams(inputStream, out);
final MasterReport report = loadReport(out.toByteArray(), path);
final int index = context.addMasterReport(report);
return context.getReportRenderContext(index);
}
finally
{
inputStream.close();
}
}
private static MasterReport loadReport(final byte[] data, final String fileName) throws
IOException, ResourceException
{
if (data == null)
{
throw new NullPointerException();
}
final ResourceManager resourceManager = new ResourceManager();
final MasterReport resource = OpenReportAction.loadReport(data, resourceManager);
resource.setAttribute(ReportDesignerBoot.DESIGNER_NAMESPACE, "report-save-path", fileName); // NON-NLS
return resource;
}
public static void launchReportOnServer(final String baseUrl, final String path) throws IOException
{
if (StringUtils.isEmpty(path))
{
throw new IOException("Path is empty.");
}
final Configuration config = ReportDesignerBoot.getInstance().getGlobalConfig();
final String urlMessage = config.getConfigProperty
("org.pentaho.reporting.designer.extensions.pentaho.repository.LaunchReport");
final String fullRepoViewerPath = MessageFormat.format(urlMessage,URLEncoder.encode(RepositoryPathEncoder.encodeRepositoryPath(path), "UTF-8"));
final String url = baseUrl + fullRepoViewerPath;
ExternalToolLauncher.openURL(url);
}
public static byte[] createBundleData(final MasterReport report) throws PublishException, BundleWriterException
{
try
{
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BundleWriter.writeReportToZipStream(report, outputStream);
return outputStream.toByteArray();
}
catch (final ContentIOException e)
{
throw new BundleWriterException("Failed to write report", e);
}
catch (final IOException e)
{
throw new BundleWriterException("Failed to write report", e);
}
}
public static int publish(final byte[] data,
final String path,
final AuthenticationData loginData)
throws IOException
{
int responseCode = HTTP_RESPONSE_FAIL;
final String versionText = loginData.getOption(SERVER_VERSION);
final int version = ParserUtil.parseInt(versionText, SERVER_VERSION_SUGAR);
if (SERVER_VERSION_SUGAR == version) {
PublishRestUtil publishRestUtil = new PublishRestUtil(loginData.getUrl(), loginData.getUsername(), loginData.getPassword());
responseCode = publishRestUtil.publishFile(path, data, true);
} else {
final FileObject connection = createVFSConnection(loginData);
final FileObject object = connection.resolveFile(path);
final OutputStream out = object.getContent().getOutputStream(false);
try
{
out.write(data);
responseCode = HTTP_RESPONSE_OK;
}
finally
{
out.close();
}
}
return responseCode;
}
public static boolean acceptFilter(final String[] filters, final String name)
{
if (filters == null || filters.length == 0)
{
return true;
}
for (int i = 0; i < filters.length; i++)
{
if (name.endsWith(filters[i]))
{
return true;
}
}
return false;
}
public static FileObject createVFSConnection(final AuthenticationData loginData) throws FileSystemException
{
return createVFSConnection(VFS.getManager(), loginData);
}
public static FileObject createVFSConnection(final FileSystemManager fileSystemManager,
final AuthenticationData loginData) throws FileSystemException
{
if (fileSystemManager == null)
{
throw new NullPointerException();
}
if (loginData == null)
{
throw new NullPointerException();
}
final String versionText = loginData.getOption(SERVER_VERSION);
final int version = ParserUtil.parseInt(versionText, SERVER_VERSION_SUGAR);
final String normalizedUrl = normalizeURL(loginData.getUrl(), version);
final FileSystemOptions fileSystemOptions = new FileSystemOptions();
final PentahoSolutionsFileSystemConfigBuilder configBuilder = new PentahoSolutionsFileSystemConfigBuilder();
configBuilder.setTimeOut(fileSystemOptions, getTimeout(loginData) * 1000);
configBuilder.setUserAuthenticator(fileSystemOptions, new StaticUserAuthenticator(normalizedUrl,
loginData.getUsername(), loginData.getPassword()));
return fileSystemManager.resolveFile(normalizedUrl, fileSystemOptions);
}
public static int getTimeout(final AuthenticationData loginData)
{
final String s = loginData.getOption(TIMEOUT);
return ParserUtil.parseInt(s, WorkspaceSettings.getInstance().getConnectionTimeout());
}
public static String normalizeURL(final String baseURL, final int version)
{
if (baseURL == null)
{
throw new NullPointerException();
}
final StringBuilder prefix = new StringBuilder(100);
final String url2;
if (version == SERVER_VERSION_LEGACY)
{
if (baseURL.toLowerCase(Locale.ENGLISH).startsWith("http://")) // NON-NLS
{
url2 = baseURL.substring("http://".length());// NON-NLS
prefix.append(WEB_SOLUTION_PREFIX);
prefix.append("http://");// NON-NLS
}
else if (baseURL.toLowerCase(Locale.ENGLISH).startsWith("https://"))// NON-NLS
{
url2 = baseURL.substring("https://".length());// NON-NLS
prefix.append(WEB_SOLUTION_PREFIX);
prefix.append("https://");// NON-NLS
}
else
{
throw new IllegalArgumentException("Not a expected URL");
}
}
else
{
if (baseURL.toLowerCase(Locale.ENGLISH).startsWith("http://")) // NON-NLS
{
url2 = baseURL.substring("http://".length());// NON-NLS
prefix.append(JCR_SOLUTION_PREFIX);
prefix.append("http://");// NON-NLS
}
else if (baseURL.toLowerCase(Locale.ENGLISH).startsWith("https://"))// NON-NLS
{
url2 = baseURL.substring("https://".length());// NON-NLS
prefix.append(JCR_SOLUTION_PREFIX);
prefix.append("https://");// NON-NLS
}
else
{
throw new IllegalArgumentException("Not a expected URL");
}
}
return prefix.append(url2).toString();
}
private static Pattern makePattern(String reservedChars)
{
// escape all reserved characters as they may have special meaning to regex engine
StringBuilder buf = new StringBuilder();
buf.append(".*["); //$NON-NLS-1$
for (int i=0;i<reservedChars.length();i++)
{
buf.append( "\\" ); //$NON-NLS-1$
buf.append(reservedChars.substring(i, i + 1));
}
buf.append("]+.*"); //$NON-NLS-1$
return Pattern.compile(buf.toString());
}
/**
* Checks for presence of black listed chars as well as illegal permutations of legal chars.
*/
public static boolean validateName(final String name)
{
return !StringUtils.isEmpty(name, true) &&
name.trim().equals( name ) && // no leading or trailing whitespace
!containsReservedCharsPattern.matcher( name ).matches() && // no reserved characters
!".".equals( name ) && // no . //$NON-NLS-1$
!"..".equals( name ) ; // no .. //$NON-NLS-1$
}
public static void setReservedChars(String reservedChars)
{
containsReservedCharsPattern = makePattern( reservedChars );
}
public static Pattern getPattern()
{
return containsReservedCharsPattern;
}
public static String getReservedCharsDisplay()
{
return reservedCharsDisplay;
}
public static void setReservedCharsDisplay(String reservedCharsDisplay)
{
PublishUtil.reservedCharsDisplay = reservedCharsDisplay;
}
}