Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve resolution and loading error messages #680

Merged
merged 3 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

package org.eclipse.esmf.aspectmodel;

@Deprecated( forRemoval = true )
public class MissingMetaModelVersionException extends RuntimeException {
private static final long serialVersionUID = -6978063564517733205L;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

package org.eclipse.esmf.aspectmodel;

@Deprecated( forRemoval = true )
public class MultipleMetaModelVersionsException extends RuntimeException {
private static final long serialVersionUID = -592452975353816247L;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
Expand All @@ -37,6 +36,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.eclipse.esmf.aspectmodel.AspectLoadingException;
import org.eclipse.esmf.aspectmodel.AspectModelFile;
import org.eclipse.esmf.aspectmodel.RdfUtil;
import org.eclipse.esmf.aspectmodel.resolver.AspectModelFileLoader;
Expand Down Expand Up @@ -208,14 +208,14 @@ public AspectModel load( final InputStream inputStream ) {
*/
public AspectModel loadNamespacePackage( final File namespacePackage ) {
if ( !namespacePackage.exists() || !namespacePackage.isFile() ) {
throw new ModelResolutionException( "The specified file does not exist or is not a file." );
throw new AspectLoadingException( "The specified file does not exist or is not a file." );
}

try ( final InputStream inputStream = new FileInputStream( namespacePackage ) ) {
return loadNamespacePackage( inputStream );
} catch ( final IOException exception ) {
LOG.error( "Error reading the file: {}", namespacePackage.getAbsolutePath(), exception );
throw new ModelResolutionException( "Error reading the file: " + namespacePackage.getAbsolutePath(), exception );
throw new AspectLoadingException( "Error reading the file: " + namespacePackage.getAbsolutePath(), exception );
}
}

Expand All @@ -232,7 +232,7 @@ public AspectModel loadNamespacePackage( final InputStream inputStream ) {
inputStream.transferTo( baos );
hasAspectModelsFolder = containsFolderInNamespacePackage( new ByteArrayInputStream( baos.toByteArray() ) );
} catch ( final IOException exception ) {
throw new ModelResolutionException( "Could not read from input", exception );
throw new AspectLoadingException( "Could not read from input", exception );
}
return loadNamespacePackageFromStream( new ByteArrayInputStream( baos.toByteArray() ), hasAspectModelsFolder );
}
Expand All @@ -258,7 +258,7 @@ private AspectModel loadNamespacePackageFromStream( final InputStream inputStrea
zis.closeEntry();
} catch ( final IOException exception ) {
LOG.error( "Error reading the Archive input stream", exception );
throw new ModelResolutionException( "Error reading the Archive input stream", exception );
throw new AspectLoadingException( "Error reading the Archive input stream", exception );
}

final LoaderContext loaderContext = new LoaderContext();
Expand Down Expand Up @@ -336,13 +336,14 @@ private Optional<AspectModelFile> applyResolutionStrategy( final String urn ) {
}
final AspectModelFile resolutionResult = resolutionStrategy.apply( aspectModelUrn, this );
if ( !containsType( resolutionResult.sourceModel(), urn ) ) {
throw new ModelResolutionException(
throw new AspectLoadingException(
"Resolution strategy returned a model which does not contain element definition for " + urn );
}
return Optional.of( resolutionResult );
} catch ( final UrnSyntaxException e ) {
} catch ( final UrnSyntaxException exception ) {
// This happens if the URN to load is no actual URN.
// If it's no valid Aspect Model URN but some other URI (e.g., a samm:see value), there is nothing
// to resolve, so we return just an empty model
// to resolve, so we return just an empty model.
return Optional.empty();
}
}
Expand Down Expand Up @@ -379,6 +380,7 @@ private void resolve( final List<AspectModelFile> inputFiles, final LoaderContex
context.unresolvedFiles().push( aspectModelFile );
}

final List<ModelResolutionException.LoadingFailure> loadingFailures = new ArrayList<>();
while ( !context.unresolvedFiles().isEmpty() || !context.unresolvedUrns().isEmpty() ) {
if ( !context.unresolvedFiles().isEmpty() ) {
final AspectModelFile modelFile = context.unresolvedFiles().pop();
Expand All @@ -389,11 +391,25 @@ private void resolve( final List<AspectModelFile> inputFiles, final LoaderContex
}

while ( !context.unresolvedUrns().isEmpty() ) {
applyResolutionStrategy( context.unresolvedUrns().pop() )
.map( this::migrate )
.ifPresent( resolvedFile -> markModelFileAsLoaded( resolvedFile, context ) );
try {
applyResolutionStrategy( context.unresolvedUrns().pop() )
.map( this::migrate )
.ifPresent( resolvedFile -> markModelFileAsLoaded( resolvedFile, context ) );
} catch ( final ModelResolutionException exception ) {
// If one element can not be resolved, collect its cause and continue, so that
// we can create an comprehensive overview of all elements that can not be resolved
if ( exception.getCheckedLocations().isEmpty() ) {
throw exception;
} else {
loadingFailures.addAll( exception.getCheckedLocations() );
}
}
}
}

if ( !loadingFailures.isEmpty() ) {
throw new ModelResolutionException( loadingFailures );
}
}

/**
Expand Down Expand Up @@ -442,7 +458,6 @@ public AspectModel loadAspectModelFiles( final Collection<AspectModelFile> input

final List<ModelElement> elements = new ArrayList<>();
final List<AspectModelFile> files = new ArrayList<>();
final Map<AspectModelFile, MetaModelBaseAttributes> namespaceDefinitions = new HashMap<>();
for ( final AspectModelFile file : inputFiles ) {
final DefaultAspectModelFile aspectModelFile = new DefaultAspectModelFile( file.sourceModel(), file.headerComment(),
file.sourceLocation() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
import java.util.Map;
import java.util.Optional;

import org.eclipse.esmf.aspectmodel.AspectLoadingException;
import org.eclipse.esmf.aspectmodel.loader.DefaultPropertyWrapper;
import org.eclipse.esmf.aspectmodel.loader.Instantiator;
import org.eclipse.esmf.aspectmodel.loader.MetaModelBaseAttributes;
import org.eclipse.esmf.aspectmodel.loader.ModelElementFactory;
import org.eclipse.esmf.aspectmodel.resolver.exceptions.InvalidModelException;
import org.eclipse.esmf.metamodel.Characteristic;
import org.eclipse.esmf.metamodel.Property;
import org.eclipse.esmf.metamodel.Scalar;
Expand Down Expand Up @@ -81,7 +81,7 @@ public Property apply( final Resource property ) {
.flatMap( statement -> characteristic.getDataType()
.map( type -> {
if ( !type.is( Scalar.class ) ) {
throw new InvalidModelException( "Type of example value on Property " + property + " has incorrect type" );
throw new AspectLoadingException( "Type of example value on Property " + property + " has incorrect type" );
}
return type.as( Scalar.class );
} )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
Expand All @@ -34,8 +35,10 @@

import org.eclipse.esmf.aspectmodel.AspectModelFile;
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ModelResolutionException;
import org.eclipse.esmf.aspectmodel.resolver.modelfile.RawAspectModelFile;
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;

import io.vavr.control.Try;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
Expand Down Expand Up @@ -160,36 +163,54 @@ public AspectModelFile apply( final AspectModelUrn aspectModelUrn, final Resolut
aspectModelUrn.getNamespaceMainPart(), aspectModelUrn.getVersion() );
final URL namedResourceFile = resourceUrl( directory, aspectModelUrn.getName() + ".ttl" );

if ( namedResourceFile != null ) {
return AspectModelFileLoader.load( namedResourceFile );
final List<ModelResolutionException.LoadingFailure> checkedLocations = new ArrayList<>();
final Try<RawAspectModelFile> tryFile = Try.of( () -> AspectModelFileLoader.load( namedResourceFile ) );
if ( tryFile.isFailure() ) {
checkedLocations.add(
new ModelResolutionException.LoadingFailure( aspectModelUrn, "Class path file "
+ Optional.ofNullable( namedResourceFile ).map( URL::toString ).orElse( aspectModelUrn.getName() + ".ttl" ),
tryFile.getCause().getMessage(), tryFile.getCause() ) );
} else {
return tryFile.get();
}

LOG.warn( "Looking for {}, but no {}.ttl was found. Inspecting files in {}", aspectModelUrn.getName(),
aspectModelUrn.getName(), directory );

return filesInDirectory( directory )
for ( final Iterator<URL> it = filesInDirectory( directory )
.filter( name -> name.endsWith( ".ttl" ) )
.map( name -> resourceUrl( directory, name ) )
.sorted( Comparator.comparing( URL::getPath ) )
.map( AspectModelFileLoader::load )
.filter( aspectModelFile -> resolutionStrategySupport.containsDefinition( aspectModelFile, aspectModelUrn ) )
.findFirst()
.orElseThrow( () -> new ModelResolutionException(
"No model file containing " + aspectModelUrn + " could be found in directory: " + directory ) );
.iterator(); it.hasNext(); ) {
final URL url = it.next();
final Try<RawAspectModelFile> file = Try.of( () -> AspectModelFileLoader.load( url ) );
if ( file.isFailure() ) {
checkedLocations.add( new ModelResolutionException.LoadingFailure( aspectModelUrn, url.toString(),
"Could not load file", file.getCause() ) );
continue;
}
final AspectModelFile result = file.get();
if ( resolutionStrategySupport.containsDefinition( result, aspectModelUrn ) ) {
return result;
}
checkedLocations.add( new ModelResolutionException.LoadingFailure( aspectModelUrn, url.toString(),
"File does not contain the element definition" ) );
}
throw new ModelResolutionException( checkedLocations );
}

private URL toUrl( final URI uri ) {
try {
return uri.toURL();
} catch ( final MalformedURLException e ) {
} catch ( final MalformedURLException exception ) {
throw new ModelResolutionException( "Could not translate URI to URL: " + uri );
}
}

private URI toUri( final URL url ) {
try {
return url.toURI();
} catch ( final URISyntaxException e ) {
} catch ( final URISyntaxException exception ) {
throw new ModelResolutionException( "Could not translate URL to URI: " + url );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public static String executeCommand( String command ) {
throw new ModelResolutionException( getOutputFrom( p.getErrorStream() ) );
}
return getOutputFrom( p.getInputStream() );
} catch ( final IOException | InterruptedException e ) {
throw new ModelResolutionException( "The attempt to execute external resolver failed with the error:", e );
} catch ( final IOException | InterruptedException exception ) {
throw new ModelResolutionException( "The attempt to execute external resolver failed with the error:", exception );
}
}

Expand All @@ -51,7 +51,8 @@ private static boolean isJarInvocation( final String command ) {
}

private static String getOutputFrom( final InputStream stream ) {
final Scanner s = new Scanner( stream ).useDelimiter( "\\A" );
return s.hasNext() ? s.next() : "";
try ( final Scanner s = new Scanner( stream ).useDelimiter( "\\A" ) ) {
return s.hasNext() ? s.next() : "";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,25 @@ public Download() {
this( ProxyConfig.detectProxySettings() );
}

/**
* Download the file and return the HTTP response
*
* @param fileUrl the URL
* @param headers list of additional headers to set
* @return the response
*/
public byte[] downloadFileContent( final URL fileUrl, final Map<String, String> headers ) {
return downloadFileAsResponse( fileUrl, headers ).body();
}

/**
* Download the file and return the contents as byte array
*
* @param fileUrl the URL
* @param headers list of additional headers to set
* @return the file contents
*/
public byte[] downloadFile( final URL fileUrl, final Map<String, String> headers ) {
public HttpResponse<byte[]> downloadFileAsResponse( final URL fileUrl, final Map<String, String> headers ) {
try {
final HttpClient.Builder clientBuilder = HttpClient.newBuilder()
.version( HttpClient.Version.HTTP_1_1 )
Expand All @@ -70,8 +81,7 @@ public byte[] downloadFile( final URL fileUrl, final Map<String, String> headers
.uri( fileUrl.toURI() )
.headers( headersArray )
.build();
final HttpResponse<byte[]> response = client.send( request, HttpResponse.BodyHandlers.ofByteArray() );
return response.body();
return client.send( request, HttpResponse.BodyHandlers.ofByteArray() );
} catch ( final InterruptedException | URISyntaxException | IOException exception ) {
throw new ModelResolutionException( "Could not retrieve " + fileUrl, exception );
}
Expand All @@ -84,7 +94,7 @@ public byte[] downloadFile( final URL fileUrl, final Map<String, String> headers
* @return the file contents
*/
public byte[] downloadFile( final URL fileUrl ) {
return downloadFile( fileUrl, Map.of() );
return downloadFileContent( fileUrl, Map.of() );
}

/**
Expand All @@ -100,8 +110,12 @@ public File downloadFile( final URL fileUrl, final File outputFile ) {

public File downloadFile( final URL fileUrl, final Map<String, String> headers, final File outputFile ) {
try ( final FileOutputStream outputStream = new FileOutputStream( outputFile ) ) {
final byte[] fileContent = downloadFile( fileUrl, headers );
outputStream.write( fileContent );
final HttpResponse<byte[]> httpResponse = downloadFileAsResponse( fileUrl, headers );
if ( httpResponse.statusCode() >= 200 && httpResponse.statusCode() < 300 ) {
outputStream.write( httpResponse.body() );
} else {
throw new ModelResolutionException( "Could not download file (status code: " + httpResponse.statusCode() + ")" );
}
} catch ( final IOException exception ) {
throw new ModelResolutionException( "Could not write file " + outputFile, exception );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
package org.eclipse.esmf.aspectmodel.resolver;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.esmf.aspectmodel.AspectModelFile;
Expand Down Expand Up @@ -45,14 +45,18 @@ public EitherStrategy( final ResolutionStrategy strategy1, final ResolutionStrat

@Override
public AspectModelFile apply( final AspectModelUrn input, final ResolutionStrategySupport resolutionStrategySupport ) {
return strategies.stream()
.map( strategy -> Try.of( () -> strategy.apply( input, resolutionStrategySupport ) ) )
.filter( Try::isSuccess )
.findFirst()
.map( Try::get )
.orElseThrow( () ->
new ModelResolutionException( "No strategy could resolve the input: " + strategies.stream().map( Object::toString )
.collect( Collectors.joining() ) ) );
final List<ModelResolutionException.LoadingFailure> checkedLocations = new ArrayList<>();
for ( final ResolutionStrategy strategy : strategies ) {
final Try<AspectModelFile> tryFile = Try.of( () -> strategy.apply( input, resolutionStrategySupport ) );
if ( tryFile.isFailure() ) {
if ( tryFile.getCause() instanceof final ModelResolutionException modelResolutionException ) {
checkedLocations.addAll( modelResolutionException.getCheckedLocations() );
}
continue;
}
return tryFile.get();
}
throw new ModelResolutionException( checkedLocations );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.stream.Stream;

import org.eclipse.esmf.aspectmodel.AspectModelFile;
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ModelResolutionException;
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;

/**
Expand All @@ -31,9 +32,15 @@ public ExternalResolverStrategy( final String command ) {

@Override
public AspectModelFile apply( final AspectModelUrn aspectModelUrn, final ResolutionStrategySupport resolutionStrategySupport ) {
final String commandWithParameters = command + " " + aspectModelUrn.toString();
final String result = CommandExecutor.executeCommand( commandWithParameters );
return AspectModelFileLoader.load( result );
try {
final String commandWithParameters = command + " " + aspectModelUrn.toString();
final String result = CommandExecutor.executeCommand( commandWithParameters );
return AspectModelFileLoader.load( result );
} catch ( final ModelResolutionException exception ) {
final ModelResolutionException.LoadingFailure failure = new ModelResolutionException.LoadingFailure(
aspectModelUrn, "The output of '" + command + "'", "Command evaluation failed", exception );
throw new ModelResolutionException( failure );
}
}

@Override
Expand Down
Loading
Loading