/*
* 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) 2006 - 2013 Pentaho Corporation and Contributors. All rights reserved.
*/
package org.pentaho.reporting.libraries.resourceloader.loader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.base.util.IOUtils;
import org.pentaho.reporting.libraries.resourceloader.LibLoaderBoot;
import org.pentaho.reporting.libraries.resourceloader.ResourceData;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceLoadingException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
/**
* A generic read handler for URL resources.
*
* @author Thomas Morgner
*/
@SuppressWarnings( "UnnecessaryBoxing" )
public class URLResourceData extends AbstractResourceData {
private static final Log logger = LogFactory.getLog( URLResourceData.class );
private static final long serialVersionUID = -7183025686032509509L;
private static Long fixedCacheDelay;
private long lastDateMetaDataRead;
private long modificationDate;
private String filename;
private Long contentLength;
private String contentType;
private boolean metaDataOK;
private URL url;
private ResourceKey key;
private static Boolean fixBrokenWebServiceDateHeader;
protected static long getFixedCacheDelay() {
if ( fixedCacheDelay == null ) {
fixedCacheDelay = new Long( LibLoaderBoot.getInstance().getExtendedConfig().getIntProperty
( "org.pentaho.reporting.libraries.resourceloader.config.url.FixedCacheDelay", 5000 ) );
}
return fixedCacheDelay.longValue();
}
protected static boolean isFixBrokenWebServiceDateHeader() {
if ( fixBrokenWebServiceDateHeader == null ) {
fixBrokenWebServiceDateHeader = Boolean.valueOf( LibLoaderBoot.getInstance().getExtendedConfig().getBoolProperty
( "org.pentaho.reporting.libraries.resourceloader.config.url.FixBrokenWebServiceDateHeader", false ) );
}
return fixBrokenWebServiceDateHeader.booleanValue();
}
public URLResourceData( final ResourceKey key ) {
if ( key == null ) {
throw new NullPointerException();
}
this.modificationDate = -1;
this.key = key;
this.url = (URL) key.getIdentifier();
// for the ease of implementation, we take the file name from the URL.
// Feel free to add a 'Content-Disposition' parser with all details :)
this.filename = IOUtils.getInstance().getFileName( url );
}
protected void setUrl( final URL url ) {
this.url = url;
}
protected void setKey( final ResourceKey key ) {
this.key = key;
}
protected void setFilename( final String filename ) {
this.filename = filename;
}
protected URL getUrl() {
return url;
}
protected String getFilename() {
return filename;
}
private void readMetaData() throws IOException {
if ( metaDataOK ) {
if ( ( System.currentTimeMillis() - lastDateMetaDataRead ) < URLResourceData.getFixedCacheDelay() ) {
return;
}
if ( isFixBrokenWebServiceDateHeader() ) {
return;
}
}
final URLConnection c = url.openConnection();
c.setDoOutput( false );
c.setAllowUserInteraction( false );
if ( c instanceof HttpURLConnection ) {
final HttpURLConnection httpURLConnection = (HttpURLConnection) c;
httpURLConnection.setRequestMethod( "HEAD" );
}
c.connect();
readMetaData( c );
c.getInputStream().close();
}
private void readMetaData( final URLConnection c ) {
modificationDate = c.getHeaderFieldDate( "last-modified", -1 );
if ( modificationDate <= 0 ) {
if ( isFixBrokenWebServiceDateHeader() ) {
modificationDate = System.currentTimeMillis();
} else {
modificationDate = -1;
}
}
contentLength = new Long( c.getContentLength() );
contentType = c.getHeaderField( "content-type" );
metaDataOK = true;
lastDateMetaDataRead = System.currentTimeMillis();
}
public InputStream getResourceAsStream( final ResourceManager caller ) throws ResourceLoadingException {
try {
final URLConnection c = url.openConnection();
c.setDoOutput( false );
c.setAllowUserInteraction( false );
c.connect();
if ( isFixBrokenWebServiceDateHeader() == false ) {
readMetaData( c );
}
return c.getInputStream();
} catch ( IOException e ) {
throw new ResourceLoadingException( "Failed to open URL connection", e );
}
}
public Object getAttribute( final String key ) {
if ( key.equals( ResourceData.FILENAME ) ) {
return filename;
}
if ( key.equals( ResourceData.CONTENT_LENGTH ) ) {
try {
if ( metaDataOK == false ) {
readMetaData();
}
return contentLength;
} catch ( IOException e ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "No response metadata could be read from the input stream", e );
}
return null;
}
}
if ( key.equals( ResourceData.CONTENT_TYPE ) ) {
try {
if ( metaDataOK == false ) {
readMetaData();
}
return contentType;
} catch ( IOException e ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "No response metadata could be read from the input stream", e );
}
return null;
}
}
return null;
}
public long getVersion( final ResourceManager caller )
throws ResourceLoadingException {
try {
// always read the new date .. sorry, this is expensive, but needed here
// else the cache would not be in sync ...
readMetaData();
return modificationDate;
} catch ( IOException e ) {
if ( logger.isDebugEnabled() ) {
logger.debug( "No response metadata could be read from the input stream", e );
}
return -1;
}
}
public ResourceKey getKey() {
return key;
}
}