/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.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 General Public License for more details.
*
*
* Copyright 2006 - 2013 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.platform.repository2.unified.fs;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.commons.lang.StringUtils;
import org.pentaho.platform.api.locale.IPentahoLocale;
import org.pentaho.platform.api.repository2.unified.IRepositoryFileData;
import org.pentaho.platform.api.repository2.unified.RepositoryFile;
import org.pentaho.platform.api.repository2.unified.RepositoryFileAcl;
import org.pentaho.platform.api.repository2.unified.RepositoryFileTree;
import org.pentaho.platform.api.repository2.unified.RepositoryRequest;
import org.pentaho.platform.api.repository2.unified.UnifiedRepositoryException;
import org.pentaho.platform.api.repository2.unified.VersionSummary;
import org.pentaho.platform.api.repository2.unified.data.node.NodeRepositoryFileData;
import org.pentaho.platform.api.repository2.unified.data.simple.SimpleRepositoryFileData;
import org.pentaho.platform.repository.RepositoryFilenameUtils;
import org.pentaho.platform.repository2.unified.IRepositoryFileDao;
import org.pentaho.platform.util.RepositoryPathEncoder;
@SuppressWarnings( "nls" )
public class FileSystemRepositoryFileDao implements IRepositoryFileDao {
private File rootDir = new File( System.getProperty( "solution.root.dir", System.getProperty( "user.dir" ) ) );
private static List<Character> reservedChars = Collections.unmodifiableList( Arrays.asList( new Character[] {
'/', '\\', '\t', '\r', '\n' } ) );
private static List<Character> reservedCharsWindows = Collections.unmodifiableList( Arrays.asList( new Character[]{
'?', '*', ':', '<', '>', '|'} ) );
public FileSystemRepositoryFileDao() {
this( new File( System.getProperty( "solution.root.dir", System.getProperty( "user.dir" ) ) ) );
}
public FileSystemRepositoryFileDao( final String baseDir ) {
this( new File( baseDir ) );
}
public FileSystemRepositoryFileDao( File baseDir ) {
// Detect OS
final String os = System.getProperty( "os.name" ).toLowerCase();
if ( os.contains( "win" ) && baseDir.getPath().equals( "\\" ) ) {
baseDir = new File( "C:\\" );
}
assert ( baseDir.exists() && baseDir.isDirectory() );
this.rootDir = baseDir;
}
public boolean canUnlockFile( Serializable fileId ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public File getRootDir() {
return new File( rootDir.getAbsolutePath() );
}
private byte[] inputStreamToBytes( InputStream in ) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream( 4096 );
byte[] buffer = new byte[4096];
int len;
while ( ( len = in.read( buffer ) ) >= 0 ) {
out.write( buffer, 0, len );
}
in.close();
out.close();
return out.toByteArray();
}
public RepositoryFile createFile( Serializable parentFolderId, RepositoryFile file, IRepositoryFileData data,
RepositoryFileAcl acl, String versionMessage ) {
String fileNameWithPath = RepositoryFilenameUtils.concat( parentFolderId.toString(), file.getName() );
FileOutputStream fos = null;
File f = new File( fileNameWithPath );
try {
f.createNewFile();
fos = new FileOutputStream( f );
if ( data instanceof SimpleRepositoryFileData ) {
fos.write( inputStreamToBytes( ( (SimpleRepositoryFileData) data ).getInputStream() ) );
} else if ( data instanceof NodeRepositoryFileData ) {
fos.write( inputStreamToBytes( new ByteArrayInputStream( ( (NodeRepositoryFileData) data ).getNode().toString()
.getBytes() ) ) );
}
} catch ( FileNotFoundException e ) {
throw new UnifiedRepositoryException( "Error writing file [" + fileNameWithPath + "]", e );
} catch ( IOException e ) {
throw new UnifiedRepositoryException( "Error writing file [" + fileNameWithPath + "]", e );
} finally {
IOUtils.closeQuietly( fos );
}
return internalGetFile( f );
}
public RepositoryFile createFolder( Serializable parentFolderId, RepositoryFile file, RepositoryFileAcl acl,
String versionMessage ) {
try {
String folderNameWithPath = parentFolderId + "/" + file.getName();
File newFolder = new File( folderNameWithPath );
newFolder.mkdir();
final RepositoryFile repositoryFolder = internalGetFile( newFolder );
return repositoryFolder;
} catch ( Throwable th ) {
throw new UnifiedRepositoryException();
}
}
public void deleteFile( Serializable fileId, String versionMessage ) {
try {
File f = new File( fileId.toString() );
f.delete();
} catch ( Exception e ) {
// CHECKSTYLES IGNORE
}
}
public void deleteFileAtVersion( Serializable fileId, Serializable versionId ) {
deleteFile( fileId, null );
}
@Override
public List<RepositoryFile> getChildren( RepositoryRequest repositoryRequest ) {
List<RepositoryFile> children = new ArrayList<RepositoryFile>();
File folder = new File( getPhysicalFileLocation( repositoryRequest.getPath() ) );
for ( Iterator<File> iterator = FileUtils.listFiles( folder, new WildcardFileFilter( repositoryRequest.getChildNodeFilter() ), null ).iterator(); iterator
.hasNext(); ) {
children.add( internalGetFile( (File) iterator.next() ) );
}
return children;
}
@Deprecated
public List<RepositoryFile> getChildren( Serializable folderId ) {
return getChildren(folderId, "", false);
}
@Deprecated
public List<RepositoryFile> getChildren( Serializable folderId, String filter ) {
return getChildren(folderId, filter, false);
}
@Deprecated
public List<RepositoryFile> getChildren( Serializable folderId, String filter, Boolean showHiddenFiles ) {
return getChildren(new RepositoryRequest(folderId.toString(), showHiddenFiles, -1, filter));
}
@SuppressWarnings( "unchecked" )
public <T extends IRepositoryFileData> T getData( Serializable fileId, Serializable versionId, Class<T> dataClass ) {
File f = new File( fileId.toString() );
T data = null;
try {
if( SimpleRepositoryFileData.class.getName().equals( dataClass.getName() ) ){
data = (T) new SimpleRepositoryFileData ( new FileInputStream( f ), "UTF-8", "text/plain" );
} else if( NodeRepositoryFileData.class.getName().equals( dataClass.getName() ) ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
} catch ( FileNotFoundException e ) {
throw new UnifiedRepositoryException( e );
}
return data;
}
public List<RepositoryFile> getDeletedFiles( Serializable folderId, String filter ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public List<RepositoryFile> getDeletedFiles() {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public RepositoryFile internalGetFile( File f ) {
RepositoryFile file = null;
if ( f.exists() ) {
String jcrPath = f.getAbsolutePath().substring( rootDir.getAbsolutePath().length() );
if ( jcrPath.length() == 0 ) {
jcrPath = "/";
}
file =
new RepositoryFile.Builder( f.getAbsolutePath(), f.getName() ).createdDate( new Date( f.lastModified() ) )
.lastModificationDate( new Date( f.lastModified() ) ).folder( f.isDirectory() ).versioned( false ).path(
jcrPath ).versionId( f.getName() ).locked( false ).lockDate( null ).lockMessage( null ).lockOwner(
null ).title( f.getName() ).description( f.getName() ).locale( null ).fileSize( f.length() ).build();
}
return file;
}
public RepositoryFile getFile( String relPath ) {
return internalGetFile( new File( getPhysicalFileLocation( relPath ) ) );
}
static String idToPath( String relPath ) {
relPath = RepositoryPathEncoder.decodeRepositoryPath( relPath );
return relPath.replaceFirst( "^/?([A-z])/[/\\\\](.*)", "$1:/$2" );
}
public RepositoryFile getFile( Serializable fileId, Serializable versionId ) {
return getFile( fileId.toString() );
}
public RepositoryFile getFileByAbsolutePath( String absPath ) {
return getFile( absPath );
}
public RepositoryFile getFileById( Serializable fileId ) {
return getFile( fileId.toString() );
}
public RepositoryFile getFile( String relPath, boolean loadLocaleMaps ) {
return getFile( relPath );
}
public RepositoryFile getFileById( Serializable fileId, boolean loadLocaleMaps ) {
return getFile( fileId.toString() );
}
@Override
public RepositoryFile getFile( String relPath, IPentahoLocale locale ) {
return getFile( relPath );
}
@Override
public RepositoryFile getFileById( Serializable fileId, IPentahoLocale locale ) {
return getFile( fileId.toString() );
}
@Override
public RepositoryFile getFile( String relPath, boolean loadLocaleMaps, IPentahoLocale locale ) {
return getFile( relPath );
}
@Override
public RepositoryFile getFileById( Serializable fileId, boolean loadLocaleMaps, IPentahoLocale locale ) {
return getFile( fileId.toString() );
}
@Override
public RepositoryFileTree getTree( RepositoryRequest repositoryRequest ) {
File root = new File( getPhysicalFileLocation( repositoryRequest.getPath() ) );
//TODO ACL
return getTree( root , repositoryRequest.getDepth().intValue() ,
repositoryRequest.getChildNodeFilter(), repositoryRequest.getTypes() );
}
@Deprecated
public RepositoryFileTree getTree( String relPath, int depth, String filter, boolean showHidden ) {
File root = new File( getPhysicalFileLocation( relPath ) );
//TODO ACL
return getTree( root , depth , filter , RepositoryRequest.FILES_TYPE_FILTER.FILES_FOLDERS );
}
private RepositoryFileTree getTree( final File file, final int depth, final String childNodeFilter,
RepositoryRequest.FILES_TYPE_FILTER types ) {
RepositoryFile rootFile = internalGetFile( file );
List<RepositoryFileTree> children;
if ( depth != 0 ) {
children = new ArrayList<RepositoryFileTree>();
if ( file.isDirectory() ) {
File[] childrenArray = file.listFiles();
for( File child : childrenArray ){
if( child.isFile() ){
if( types == RepositoryRequest.FILES_TYPE_FILTER.FILES_FOLDERS || types == RepositoryRequest.FILES_TYPE_FILTER.FILES ){
children.add( new RepositoryFileTree( internalGetFile( child ), new ArrayList<RepositoryFileTree>() ) );
}
continue;
}
RepositoryFileTree repositoryChildFileTree = getTree( child, depth - 1, childNodeFilter, types );
if ( repositoryChildFileTree != null ) {
children.add( repositoryChildFileTree );
}
}
}
Collections.sort( children );
} else {
children = null;
}
return new RepositoryFileTree( rootFile, children );
}
public List<VersionSummary> getVersionSummaries( Serializable fileId ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public VersionSummary getVersionSummary( Serializable fileId, Serializable versionId ) {
RepositoryFile file = getFile( fileId, versionId );
List<String> labels = new ArrayList<String>();
return new VersionSummary(fileId, ( versionId != null ? versionId : fileId ) ,
false, file.getCreatedDate(), file.getCreatorId(), StringUtils.EMPTY, labels );
}
public void lockFile( Serializable fileId, String message ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public void moveFile( Serializable fileId, String destRelPath, String versionMessage ) {
RepositoryFile file = getFileById( fileId );
SimpleRepositoryFileData data = getData( fileId, null, SimpleRepositoryFileData.class );
deleteFile( fileId, versionMessage );
createFile( null, file, data, null, versionMessage );
}
public void permanentlyDeleteFile( Serializable fileId, String versionMessage ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public void restoreFileAtVersion( Serializable fileId, Serializable versionId, String versionMessage ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public void undeleteFile( Serializable fileId, String versionMessage ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public void unlockFile( Serializable fileId ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public RepositoryFile updateFile( RepositoryFile file, IRepositoryFileData data, String versionMessage ) {
File f = new File( file.getId().toString() );
FileOutputStream fos = null;
try {
fos = new FileOutputStream( f, false );
if ( data instanceof SimpleRepositoryFileData ) {
fos.write( inputStreamToBytes( ( (SimpleRepositoryFileData) data ).getInputStream() ) );
} else if ( data instanceof NodeRepositoryFileData ) {
fos.write( inputStreamToBytes( new ByteArrayInputStream( ( (NodeRepositoryFileData) data ).getNode().toString()
.getBytes() ) ) );
}
} catch ( FileNotFoundException e ) {
throw new UnifiedRepositoryException( e );
} catch ( IOException e ) {
throw new UnifiedRepositoryException( e );
} finally {
IOUtils.closeQuietly( fos );
}
return getFile( file.getPath() );
}
public List<RepositoryFile> getReferrers( Serializable fileId ) {
throw new UnsupportedOperationException();
}
public void setRootDir( File rootDir ) {
this.rootDir = rootDir;
}
public void setFileMetadata( final Serializable fileId, Map<String, Serializable> metadataMap ) {
final File targetFile = new File( fileId.toString() );
if ( targetFile.exists() ) {
FileOutputStream fos = null;
try {
final File metadataDir = new File( targetFile.getParent() + File.separatorChar + ".metadata" );
if ( !metadataDir.exists() ) {
metadataDir.mkdir();
}
final File metadataFile = new File( metadataDir, targetFile.getName() );
if ( !metadataFile.exists() ) {
metadataFile.createNewFile();
}
final StringBuilder data = new StringBuilder();
for ( String key : metadataMap.keySet() ) {
data.append( key ).append( '=' );
if ( metadataMap.get( key ) != null ) {
data.append( metadataMap.get( key ).toString() );
}
data.append( '\n' );
}
fos = new FileOutputStream( metadataFile );
fos.write( data.toString().getBytes() );
} catch ( FileNotFoundException e ) {
throw new UnifiedRepositoryException( "Error writing file metadata [" + fileId + "]", e );
} catch ( IOException e ) {
throw new UnifiedRepositoryException( "Error writing file metadata [" + fileId + "]", e );
} finally {
IOUtils.closeQuietly( fos );
}
}
}
public Map<String, Serializable> getFileMetadata( final Serializable fileId ) {
final String metadataFilename =
FilenameUtils.concat( FilenameUtils.concat( FilenameUtils.getFullPathNoEndSeparator( fileId.toString() ),
".metadata" ), FilenameUtils.getName( fileId.toString() ) );
final Map<String, Serializable> metadata = new HashMap<String, Serializable>();
BufferedReader reader = null;
try {
reader = new BufferedReader( new FileReader( metadataFilename ) );
String data = reader.readLine();
while ( data != null ) {
final int pos = data.indexOf( '=' );
if ( pos > 0 ) {
final String key = data.substring( 0, pos );
final String value = ( data.length() > pos ? data.substring( pos + 1 ) : null );
metadata.put( key, value );
}
data = reader.readLine();
}
} catch ( FileNotFoundException e ) {
// Do nothing ... metadata empty
} catch ( IOException e ) {
throw new UnifiedRepositoryException( "Error reading metadata [" + fileId + "]", e );
} finally {
IOUtils.closeQuietly( reader );
}
return metadata;
}
public void copyFile( Serializable fileId, String destAbsPath, String versionMessage ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
public List<RepositoryFile> getDeletedFiles( String origParentFolderPath, String filter ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public List<Character> getReservedChars() {
List<Character> charList = new ArrayList<Character>();
String osName = System.getProperty( "os.name" );
charList.addAll( reservedChars );
if ( osName.contains( "Windows" ) ) {
charList.addAll( reservedCharsWindows );
}
return charList;
}
@Override
public List<Locale> getAvailableLocalesForFileById( Serializable fileId ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public List<Locale> getAvailableLocalesForFileByPath( String relPath ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public List<Locale> getAvailableLocalesForFile( RepositoryFile repositoryFile ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public Properties getLocalePropertiesForFileById( Serializable fileId, String locale ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public Properties getLocalePropertiesForFileByPath( String relPath, String locale ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public Properties getLocalePropertiesForFile( RepositoryFile repositoryFile, String locale ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public void setLocalePropertiesForFileById( Serializable fileId, String locale, Properties properties ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public void setLocalePropertiesForFileByPath( String relPath, String locale, Properties properties ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public void setLocalePropertiesForFile( RepositoryFile repositoryFile, String locale, Properties properties ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public void deleteLocalePropertiesForFile( RepositoryFile repositoryFile, String locale ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
@Override
public RepositoryFile updateFolder( RepositoryFile file, String versionMessage ) {
throw new UnsupportedOperationException( "This operation is not support by this repository" );
}
private String getPhysicalFileLocation( String relPath ){
if( StringUtils.isEmpty( relPath ) ){
return relPath;
}
String physicalFileLocation = relPath;
if ( relPath.equals( "/" ) ) {
physicalFileLocation = rootDir.getAbsolutePath();
} else if ( relPath.startsWith( rootDir.getAbsolutePath() ) ) {
physicalFileLocation = relPath;
} else {
physicalFileLocation = RepositoryFilenameUtils.concat( rootDir.getAbsolutePath(), relPath.substring( RepositoryFilenameUtils.getPrefixLength( relPath ) ) );
}
return physicalFileLocation;
}
}