/*******************************************************************************
* Copyright (c) 2002, 2015 Innoopract Informationssysteme GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
* Frank Appel - replaced singletons and static fields (Bug 337787)
******************************************************************************/
package org.eclipse.rap.rwt.internal.resources;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.rap.rwt.internal.util.ParamCheck;
import org.eclipse.rap.rwt.internal.util.StreamUtil;
import org.eclipse.rap.rwt.service.ResourceLoader;
import org.eclipse.rap.rwt.service.ResourceManager;
/**
* The resource manager is responsible for registering resources like images,
* CSS files etc. which are available on the application's classpath. The
* registered files will be read out from their libraries and delivered if
* requested. Usually resources are stored in libraries in the WEB-INF/lib
* directory of a web-application
* <p>
* Implementation as singleton.
* </p>
* <p>
* This class is not intended to be used by clients.
* </p>
*/
public class ResourceManagerImpl implements ResourceManager {
private final ResourceDirectory resourceDirectory;
private final Set<String> resources;
public ResourceManagerImpl( ResourceDirectory resourceDirectory ) {
this.resourceDirectory = resourceDirectory;
resources = Collections.synchronizedSet( new HashSet<String>() );
}
/////////////////////////////
// interface ResourceManager
public void registerOnce( String resource, ResourceLoader loader ) {
ParamCheck.notNull( resource, "resource" );
ParamCheck.notNull( loader, "loader" );
if( !resources.contains( resource ) ) {
checkPath( resource );
InputStream stream = null;
try {
stream = loader.getResourceAsStream( resource );
internalRegister( resource, stream );
} catch( IOException ioe ) {
throw new RuntimeException( "Failed to register resource: " + resource, ioe );
} finally {
if( stream != null ) {
StreamUtil.close( stream );
}
}
}
}
@Override
public void register( String path, InputStream inputStream ) {
ParamCheck.notNull( path, "name" );
ParamCheck.notNull( inputStream, "inputStream" );
checkPath( path );
internalRegister( path, inputStream );
}
@Override
public boolean unregister( String name ) {
ParamCheck.notNull( name, "name" );
boolean result = false;
if( resources.remove( name ) ) {
result = true;
File file = getDiskLocation( name );
file.delete();
}
return result;
}
@Override
public boolean isRegistered( String name ) {
ParamCheck.notNull( name, "name" );
return resources.contains( name );
}
@Override
public String getLocation( String name ) {
ParamCheck.notNull( name, "name" );
if( !resources.contains( name ) ) {
throw new IllegalArgumentException( "Resource does not exist: " + name );
}
return createRequestUrl( name );
}
@Override
public InputStream getRegisteredContent( String name ) {
ParamCheck.notNull( name, "name" );
InputStream result = null;
if( resources.contains( name ) ) {
File file = getDiskLocation( name );
try {
result = new FileInputStream( file );
} catch( FileNotFoundException fnfe ) {
throw new RuntimeException( fnfe );
}
}
return result;
}
//////////////////
// helping methods
private static String createRequestUrl( String resourceName ) {
return new StringBuilder()
.append( ResourceDirectory.DIRNAME )
.append( "/" )
.append( escapeResourceName( resourceName.replace( '\\', '/' ) ) )
.toString();
}
private void internalRegister( String name, InputStream inputStream ) {
File location = getDiskLocation( name );
try {
createDirectories( location );
writeResource( inputStream, location );
} catch ( IOException ioe ) {
throw new RuntimeException( "Failed to register resource: " + name, ioe );
}
resources.add( name );
}
private static void writeResource( InputStream inputStream, File location )
throws IOException
{
BufferedInputStream bufferedStream = new BufferedInputStream( inputStream );
OutputStream outputStream = new BufferedOutputStream( new FileOutputStream( location ) );
try {
byte[] buffer = new byte[ 256 ];
int read = bufferedStream.read( buffer );
while( read != -1 ) {
outputStream.write( buffer, 0, read );
read = bufferedStream.read( buffer );
}
} finally {
outputStream.close();
}
}
private static void createDirectories( File file ) throws IOException {
File dir = new File( file.getParent() );
if( !dir.mkdirs() ) {
if( !dir.exists() ) {
throw new IOException( "Could not create directory structure: " + dir.getAbsolutePath() );
}
}
}
private File getDiskLocation( String resourceName ) {
String escapedResourceName = escapeResourceName( resourceName );
return new File( resourceDirectory.getDirectory(), escapedResourceName );
}
//////////////////
// helping methods
private static void checkPath( String path ) {
if( path.length() == 0 ) {
throw new IllegalArgumentException( "Path must not be empty" );
}
if( path.endsWith( "/" ) || path.endsWith( "\\" ) ) {
throw new IllegalArgumentException( "Path must not end with path separator" );
}
}
private static String escapeResourceName( String name ) {
return name
.replaceAll( "\\$", "\\$\\$" )
.replaceAll( ":", "\\$1" )
.replaceAll( "\\?", "\\$2" );
}
}