/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.guvnor.common.services.project.backend.server;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.enterprise.inject.Instance;
import org.guvnor.common.services.backend.exceptions.ExceptionUtilities;
import org.guvnor.common.services.backend.file.LinkedDirectoryFilter;
import org.guvnor.common.services.backend.file.LinkedDotFileFilter;
import org.guvnor.common.services.backend.file.LinkedMetaInfFolderFilter;
import org.guvnor.common.services.backend.util.CommentedOptionFactory;
import org.guvnor.common.services.project.model.POM;
import org.guvnor.common.services.project.model.Package;
import org.guvnor.common.services.project.model.Project;
import org.guvnor.common.services.project.service.POMService;
import org.guvnor.common.services.project.service.PackageAlreadyExistsException;
import org.guvnor.common.services.project.service.ProjectResourceResolver;
import org.guvnor.structure.backend.backcompat.BackwardCompatibleUtil;
import org.guvnor.structure.server.config.ConfigGroup;
import org.guvnor.structure.server.config.ConfigItem;
import org.guvnor.structure.server.config.ConfigType;
import org.guvnor.structure.server.config.ConfigurationService;
import org.uberfire.backend.server.util.Paths;
import org.uberfire.backend.vfs.Path;
import org.uberfire.io.IOService;
import org.uberfire.java.nio.file.DirectoryStream;
import org.uberfire.java.nio.file.FileSystem;
import org.uberfire.java.nio.file.Files;
import static org.guvnor.common.services.project.utils.ProjectResourcePaths.*;
public abstract class ResourceResolver<T extends Project>
implements ProjectResourceResolver<T> {
protected IOService ioService;
protected POMService pomService;
protected ConfigurationService configurationService;
protected CommentedOptionFactory commentedOptionFactory;
protected BackwardCompatibleUtil backward;
protected List<ProjectResourcePathResolver> resourcePathResolvers = new ArrayList<>( );
public ResourceResolver() {
}
public ResourceResolver( final IOService ioService,
final POMService pomService,
final ConfigurationService configurationService,
final CommentedOptionFactory commentedOptionFactory,
final BackwardCompatibleUtil backward ,
final Instance<ProjectResourcePathResolver> resourcePathResolversInstance ) {
this.ioService = ioService;
this.pomService = pomService;
this.configurationService = configurationService;
this.commentedOptionFactory = commentedOptionFactory;
this.backward = backward;
initResourcePathResolvers( resourcePathResolversInstance );
}
private void initResourcePathResolvers( final Instance< ProjectResourcePathResolver > resourcePathResolversInstance ) {
Optional.ofNullable( resourcePathResolversInstance.iterator() )
.ifPresent( iterator -> iterator.forEachRemaining( resolver -> resourcePathResolvers.add( resolver ) ) );
}
public Package newPackage( final Package parentPackage,
final String packageName,
final boolean startBatch ) {
//If the package name contains separators, create sub-folders
String newPackageName = packageName.toLowerCase();
if ( newPackageName.contains( "." ) ) {
newPackageName = newPackageName.replace( ".",
"/" );
}
//Return new package
final Path mainSrcPath = parentPackage.getPackageMainSrcPath();
final Path testSrcPath = parentPackage.getPackageTestSrcPath();
final Path mainResourcesPath = parentPackage.getPackageMainResourcesPath();
final Path testResourcesPath = parentPackage.getPackageTestResourcesPath();
Path pkgPath = null;
final FileSystem fs = Paths.convert( parentPackage.getPackageMainSrcPath() ).getFileSystem();
try {
if ( startBatch ) {
ioService.startBatch( fs,
commentedOptionFactory.makeCommentedOption( "New package [" + packageName + "]" ) );
}
final org.uberfire.java.nio.file.Path nioMainSrcPackagePath = Paths.convert( mainSrcPath ).resolve( newPackageName );
if ( !Files.exists( nioMainSrcPackagePath ) ) {
pkgPath = Paths.convert( ioService.createDirectory( nioMainSrcPackagePath ) );
}
final org.uberfire.java.nio.file.Path nioTestSrcPackagePath = Paths.convert( testSrcPath ).resolve( newPackageName );
if ( !Files.exists( nioTestSrcPackagePath ) ) {
pkgPath = Paths.convert( ioService.createDirectory( nioTestSrcPackagePath ) );
}
final org.uberfire.java.nio.file.Path nioMainResourcesPackagePath = Paths.convert( mainResourcesPath ).resolve( newPackageName );
if ( !Files.exists( nioMainResourcesPackagePath ) ) {
pkgPath = Paths.convert( ioService.createDirectory( nioMainResourcesPackagePath ) );
}
final org.uberfire.java.nio.file.Path nioTestResourcesPackagePath = Paths.convert( testResourcesPath ).resolve( newPackageName );
if ( !Files.exists( nioTestResourcesPackagePath ) ) {
pkgPath = Paths.convert( ioService.createDirectory( nioTestResourcesPackagePath ) );
}
//If pkgPath is null the package already existed in src/main/java, scr/main/resources, src/test/java and src/test/resources
if ( pkgPath == null ) {
throw new PackageAlreadyExistsException( packageName );
}
//Return new package
final Package newPackage = resolvePackage( pkgPath );
return newPackage;
} catch ( Exception e ) {
throw ExceptionUtilities.handleException( e );
} finally {
if ( startBatch ) {
ioService.endBatch();
}
}
}
private String getPackagePathSuffix( final org.uberfire.java.nio.file.Path nioProjectRootPath,
final org.uberfire.java.nio.file.Path nioPackagePath ) {
final org.uberfire.java.nio.file.Path nioMainSrcPath = nioProjectRootPath.resolve( MAIN_SRC_PATH );
final org.uberfire.java.nio.file.Path nioTestSrcPath = nioProjectRootPath.resolve( TEST_SRC_PATH );
final org.uberfire.java.nio.file.Path nioMainResourcesPath = nioProjectRootPath.resolve( MAIN_RESOURCES_PATH );
final org.uberfire.java.nio.file.Path nioTestResourcesPath = nioProjectRootPath.resolve( TEST_RESOURCES_PATH );
String packageName = null;
org.uberfire.java.nio.file.Path packagePath = null;
if ( nioPackagePath.startsWith( nioMainSrcPath ) ) {
packagePath = nioMainSrcPath.relativize( nioPackagePath );
packageName = packagePath.toString();
} else if ( nioPackagePath.startsWith( nioTestSrcPath ) ) {
packagePath = nioTestSrcPath.relativize( nioPackagePath );
packageName = packagePath.toString();
} else if ( nioPackagePath.startsWith( nioMainResourcesPath ) ) {
packagePath = nioMainResourcesPath.relativize( nioPackagePath );
packageName = packagePath.toString();
} else if ( nioPackagePath.startsWith( nioTestResourcesPath ) ) {
packagePath = nioTestResourcesPath.relativize( nioPackagePath );
packageName = packagePath.toString();
}
return packageName;
}
@Override
public abstract T resolveProject( final Path resource );
@Override
public Project resolveParentProject( final Path resource ) {
try {
//Null resource paths cannot resolve to a Project
if ( resource == null ) {
return null;
}
//Check if resource is the project root
org.uberfire.java.nio.file.Path path = Paths.convert( resource ).normalize();
if ( hasPom( path ) ) {
final Path projectRootPath = Paths.convert( path );
return new Project( projectRootPath,
Paths.convert( path.resolve( POM_PATH ) ),
projectRootPath.getFileName() );
} else {
return null;
}
} catch ( Exception e ) {
throw ExceptionUtilities.handleException( e );
}
}
@Override
public Project resolveToParentProject( final Path resource ) {
try {
//Null resource paths cannot resolve to a Project
if ( resource == null ) {
return null;
}
//Check if resource is the project root
org.uberfire.java.nio.file.Path path = Paths.convert( resource ).normalize();
org.uberfire.java.nio.file.Path parentPomPath = path.resolve( POM_PATH );
if ( hasPom( path ) ) {
POM parent = pomService.load( Paths.convert( parentPomPath ) );
final Path projectRootPath = Paths.convert( path );
Project project = new Project( projectRootPath,
Paths.convert( parentPomPath ),
projectRootPath.getFileName(),
parent.getModules() );
return project;
} else {
return null;
}
} catch ( Exception e ) {
throw ExceptionUtilities.handleException( e );
}
}
@Override
public Set<Package> resolvePackages( final Project project ) {
final Set<Package> packages = new HashSet<Package>();
final Set<String> packageNames = new HashSet<String>();
if ( project == null ) {
return packages;
}
//Build a set of all package names across /src/main/java, /src/main/resources, /src/test/java and /src/test/resources paths
//It is possible (if the project was not created within the workbench that some packages only exist in certain paths)
final Path projectRoot = project.getRootPath();
final org.uberfire.java.nio.file.Path nioProjectRootPath = Paths.convert( projectRoot );
for ( String src : SOURCE_PATHS ) {
final org.uberfire.java.nio.file.Path nioPackageRootSrcPath = nioProjectRootPath.resolve( src );
packageNames.addAll( getPackageNames( nioProjectRootPath,
nioPackageRootSrcPath,
true,
true,
true ) );
}
//Construct Package objects for each package name
final java.util.Set<String> resolvedPackages = new java.util.HashSet<String>();
for ( String packagePathSuffix : packageNames ) {
for ( String src : SOURCE_PATHS ) {
final org.uberfire.java.nio.file.Path nioPackagePath = nioProjectRootPath.resolve( src ).resolve( packagePathSuffix );
if ( Files.exists( nioPackagePath ) && !resolvedPackages.contains( packagePathSuffix ) ) {
packages.add( resolvePackage( Paths.convert( nioPackagePath ) ) );
resolvedPackages.add( packagePathSuffix );
}
}
}
return packages;
}
@Override
public Set<Package> resolvePackages( final Package pkg ) {
final Set<Package> packages = new HashSet<Package>();
final Set<String> packageNames = new HashSet<String>();
if ( pkg == null ) {
return packages;
}
//Build a set of all package names across /src/main/java, /src/main/resources, /src/test/java and /src/test/resources paths
//It is possible (if the project was not created within the workbench that some packages only exist in certain paths)
final Path projectRoot = pkg.getProjectRootPath();
final org.uberfire.java.nio.file.Path nioProjectRootPath = Paths.convert( projectRoot );
for ( String src : SOURCE_PATHS ) {
final org.uberfire.java.nio.file.Path nioPackageRootSrcPath = nioProjectRootPath.resolve( src ).resolve( resolvePkgName( pkg.getCaption() ) );
packageNames.addAll( getPackageNames( nioProjectRootPath,
nioPackageRootSrcPath,
false,
true,
false ) );
}
//Construct Package objects for each package name
final java.util.Set<String> resolvedPackages = new java.util.HashSet<String>();
for ( String packagePathSuffix : packageNames ) {
for ( String src : SOURCE_PATHS ) {
final org.uberfire.java.nio.file.Path nioPackagePath = nioProjectRootPath.resolve( src ).resolve( packagePathSuffix );
if ( Files.exists( nioPackagePath ) && !resolvedPackages.contains( packagePathSuffix ) ) {
packages.add( resolvePackage( Paths.convert( nioPackagePath ) ) );
resolvedPackages.add( packagePathSuffix );
}
}
}
return packages;
}
@Override
public org.guvnor.common.services.project.model.Package resolveDefaultPackage( final Project project ) {
final Set<String> packageNames = new HashSet<String>();
if ( project == null ) {
return null;
}
//Build a set of all package names across /src/main/java, /src/main/resources, /src/test/java and /src/test/resources paths
//It is possible (if the project was not created within the workbench that some packages only exist in certain paths)
final Path projectRoot = project.getRootPath();
final org.uberfire.java.nio.file.Path nioProjectRootPath = Paths.convert( projectRoot );
for ( String src : SOURCE_PATHS ) {
final org.uberfire.java.nio.file.Path nioPackageRootSrcPath = nioProjectRootPath.resolve( src );
packageNames.addAll( getPackageNames( nioProjectRootPath,
nioPackageRootSrcPath,
true,
true,
false ) );
}
//Construct Package objects for each package name
final java.util.Set<String> resolvedPackages = new java.util.HashSet<String>();
for ( String packagePathSuffix : packageNames ) {
for ( String src : SOURCE_PATHS ) {
final org.uberfire.java.nio.file.Path nioPackagePath = nioProjectRootPath.resolve( src ).resolve( packagePathSuffix );
if ( Files.exists( nioPackagePath ) && !resolvedPackages.contains( packagePathSuffix ) ) {
return resolvePackage( Paths.convert( nioPackagePath ) );
}
}
}
return null;
}
@Override
public Package resolveParentPackage( final Package pkg ) {
final Set<String> packageNames = new HashSet<String>();
//Build a set of all package names across /src/main/java, /src/main/resources, /src/test/java and /src/test/resources paths
final org.uberfire.java.nio.file.Path nioProjectRootPath = Paths.convert( pkg.getProjectRootPath() );
packageNames.addAll( getPackageNames( nioProjectRootPath,
Paths.convert( pkg.getPackageMainSrcPath() ).getParent(),
true,
false,
false ) );
packageNames.addAll( getPackageNames( nioProjectRootPath,
Paths.convert( pkg.getPackageMainResourcesPath() ).getParent(),
true,
false,
false ) );
packageNames.addAll( getPackageNames( nioProjectRootPath,
Paths.convert( pkg.getPackageTestSrcPath() ).getParent(),
true,
false,
false ) );
packageNames.addAll( getPackageNames( nioProjectRootPath,
Paths.convert( pkg.getPackageTestResourcesPath() ).getParent(),
true,
false,
false ) );
//Construct Package objects for each package name
for ( String packagePathSuffix : packageNames ) {
for ( String src : SOURCE_PATHS ) {
if ( packagePathSuffix == null ) {
return null;
}
final org.uberfire.java.nio.file.Path nioPackagePath = nioProjectRootPath.resolve( src ).resolve( packagePathSuffix );
if ( Files.exists( nioPackagePath ) ) {
return resolvePackage( Paths.convert( nioPackagePath ) );
}
}
}
return null;
}
@Override
public Path resolveDefaultPath( final Package pkg, final String resourceType ) {
final ProjectResourcePathResolver[] currentResolver = new ProjectResourcePathResolver[ 1 ];
resourcePathResolvers.forEach( resolver -> {
if ( resolver.accept( resourceType ) ) {
if ( currentResolver[ 0 ] == null || currentResolver[ 0 ].getPriority( ) < resolver.getPriority( ) ) {
currentResolver[ 0 ] = resolver;
}
}
} );
if ( currentResolver[ 0 ] == null ) {
//uncommon case, by construction the DefaultProjectResourcePathResolver is exists.
throw new RuntimeException( "No ProjectResourcePathResolver has been defined for resourceType: " + resourceType );
} else {
return currentResolver[ 0 ].resolveDefaultPath( pkg );
}
}
@Override
public boolean isPom( final Path resource ) {
try {
//Null resource paths cannot resolve to a Project
if ( resource == null ) {
return false;
}
//Check if path equals pom.xml
final Project project = resolveProject( resource );
//It's possible that the Incremental Build attempts to act on a Project file before the project has been fully created.
//This should be a short-term issue that will be resolved when saving a project batches pom.xml, kmodule.xml and project.imports
//etc into a single git-batch. At present they are saved individually leading to multiple Incremental Build requests.
if ( project == null ) {
return false;
}
final org.uberfire.java.nio.file.Path path = Paths.convert( resource ).normalize();
final org.uberfire.java.nio.file.Path pomFilePath = Paths.convert( project.getPomXMLPath() );
return path.startsWith( pomFilePath );
} catch ( Exception e ) {
throw ExceptionUtilities.handleException( e );
}
}
@Override
public org.guvnor.common.services.project.model.Package resolvePackage( final Path resource ) {
try {
//Null resource paths cannot resolve to a Project
if ( resource == null ) {
return null;
}
//If Path is not within a Project we cannot resolve a package
final Project project = resolveProject( resource );
if ( project == null ) {
return null;
}
//pom.xml is not inside a package
if ( isPom( resource ) ) {
return null;
}
return makePackage( project,
resource );
} catch ( Exception e ) {
throw ExceptionUtilities.handleException( e );
}
}
protected boolean hasPom( final org.uberfire.java.nio.file.Path path ) {
final org.uberfire.java.nio.file.Path pomPath = path.resolve( POM_PATH );
return Files.exists( pomPath );
}
protected T makeProject( final org.uberfire.java.nio.file.Path nioProjectRootPath ) {
final T project = simpleProjectInstance( nioProjectRootPath );
addSecurityGroups( project );
return project;
}
public abstract T simpleProjectInstance( final org.uberfire.java.nio.file.Path nioProjectRootPath );
protected void addSecurityGroups( final T project ) {
//Copy in Security Roles required to access this resource
final ConfigGroup projectConfiguration = findProjectConfig( project.getRootPath() );
if ( projectConfiguration != null ) {
ConfigItem<List<String>> groups = backward.compat( projectConfiguration ).getConfigItem( "security:groups" );
if ( groups != null ) {
for ( String group : groups.getValue() ) {
project.getGroups().add( group );
}
}
}
}
protected ConfigGroup findProjectConfig( final Path projectRoot ) {
final Collection<ConfigGroup> groups = configurationService.getConfiguration( ConfigType.PROJECT );
if ( groups != null ) {
for ( ConfigGroup groupConfig : groups ) {
if ( groupConfig.getName().equals( projectRoot.toURI() ) ) {
return groupConfig;
}
}
}
return null;
}
protected Package makePackage( final Project project,
final Path resource ) {
final Path projectRoot = project.getRootPath();
final org.uberfire.java.nio.file.Path nioProjectRoot = Paths.convert( projectRoot );
final org.uberfire.java.nio.file.Path nioMainSrcPath = nioProjectRoot.resolve( MAIN_SRC_PATH );
final org.uberfire.java.nio.file.Path nioTestSrcPath = nioProjectRoot.resolve( TEST_SRC_PATH );
final org.uberfire.java.nio.file.Path nioMainResourcesPath = nioProjectRoot.resolve( MAIN_RESOURCES_PATH );
final org.uberfire.java.nio.file.Path nioTestResourcesPath = nioProjectRoot.resolve( TEST_RESOURCES_PATH );
org.uberfire.java.nio.file.Path nioResource = Paths.convert( resource );
if ( Files.isRegularFile( nioResource ) ) {
nioResource = nioResource.getParent();
}
String packageName = null;
org.uberfire.java.nio.file.Path packagePath = null;
if ( nioResource.startsWith( nioMainSrcPath ) ) {
packagePath = nioMainSrcPath.relativize( nioResource );
packageName = packagePath.toString().replaceAll( "/",
"." );
} else if ( nioResource.startsWith( nioTestSrcPath ) ) {
packagePath = nioTestSrcPath.relativize( nioResource );
packageName = packagePath.toString().replaceAll( "/",
"." );
} else if ( nioResource.startsWith( nioMainResourcesPath ) ) {
packagePath = nioMainResourcesPath.relativize( nioResource );
packageName = packagePath.toString().replaceAll( "/",
"." );
} else if ( nioResource.startsWith( nioTestResourcesPath ) ) {
packagePath = nioTestResourcesPath.relativize( nioResource );
packageName = packagePath.toString().replaceAll( "/",
"." );
}
//Resource was not inside a package
if ( packageName == null ) {
return null;
}
final Path mainSrcPath = Paths.convert( nioMainSrcPath.resolve( packagePath ) );
final Path testSrcPath = Paths.convert( nioTestSrcPath.resolve( packagePath ) );
final Path mainResourcesPath = Paths.convert( nioMainResourcesPath.resolve( packagePath ) );
final Path testResourcesPath = Paths.convert( nioTestResourcesPath.resolve( packagePath ) );
final String displayName = getPackageDisplayName( packageName );
final Package pkg = new Package( project.getRootPath(),
mainSrcPath,
testSrcPath,
mainResourcesPath,
testResourcesPath,
packageName,
displayName,
getPackageRelativeCaption( displayName, resource.getFileName() ) );
return pkg;
}
private Set<String> getPackageNames( final org.uberfire.java.nio.file.Path nioProjectRootPath,
final org.uberfire.java.nio.file.Path nioPackageSrcPath,
final boolean includeDefault,
final boolean includeChild,
final boolean recursive ) {
final Set<String> packageNames = new HashSet<String>();
if ( !Files.exists( nioPackageSrcPath ) ) {
return packageNames;
}
if ( includeDefault || recursive ) {
packageNames.add( getPackagePathSuffix( nioProjectRootPath,
nioPackageSrcPath ) );
}
if ( !includeChild ) {
return packageNames;
}
//We're only interested in Directories (and not META-INF) so set-up appropriate filters
final LinkedMetaInfFolderFilter metaDataFileFilter = new LinkedMetaInfFolderFilter();
final LinkedDotFileFilter dotFileFilter = new LinkedDotFileFilter( metaDataFileFilter );
final LinkedDirectoryFilter directoryFilter = new LinkedDirectoryFilter( dotFileFilter );
final DirectoryStream<org.uberfire.java.nio.file.Path> nioChildPackageSrcPaths = ioService.newDirectoryStream( nioPackageSrcPath,
directoryFilter );
for ( org.uberfire.java.nio.file.Path nioChildPackageSrcPath : nioChildPackageSrcPaths ) {
if ( recursive ) {
packageNames.addAll( getPackageNames( nioProjectRootPath,
nioChildPackageSrcPath,
includeDefault,
includeChild,
recursive ) );
} else {
packageNames.add( getPackagePathSuffix( nioProjectRootPath,
nioChildPackageSrcPath ) );
}
}
return packageNames;
}
private String getPackageDisplayName( final String packageName ) {
return packageName.isEmpty() ? "<default>" : packageName;
}
private String getPackageRelativeCaption( final String displayName,
final String relativeName ) {
return displayName.equals( "<default>" ) ? "<default>" : relativeName;
}
private String resolvePkgName( final String caption ) {
if ( caption.equals( "<default>" ) ) {
return "";
}
return caption.replaceAll( "\\.", "/" );
}
}