Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) 2023-2024 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* 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 com.helger.phive.ves.engine.load;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.Locale;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.collection.impl.CommonsLinkedHashSet;
import com.helger.commons.collection.impl.ICommonsList;
import com.helger.commons.collection.impl.ICommonsSet;
import com.helger.commons.concurrent.SimpleReadWriteLock;
import com.helger.commons.datetime.PDTFactory;
import com.helger.commons.error.IError;
import com.helger.commons.error.SingleError;
import com.helger.commons.error.level.EErrorLevel;
import com.helger.commons.error.list.ErrorList;
import com.helger.commons.io.resource.IReadableResource;
import com.helger.commons.state.ESuccess;
import com.helger.commons.string.StringHelper;
import com.helger.commons.timing.StopWatch;
import com.helger.diver.api.coord.DVRCoordinate;
import com.helger.diver.api.version.DVRVersionException;
import com.helger.diver.repo.IRepoStorageReadItem;
import com.helger.diver.repo.RepoStorageKeyOfArtefact;
import com.helger.diver.repo.RepoStorageReadableResource;
import com.helger.diver.repo.toc.IRepoStorageWithToc;
import com.helger.phive.api.executorset.status.IValidationExecutorSetStatus;
import com.helger.phive.api.executorset.status.ValidationExecutorSetStatus;
import com.helger.phive.api.result.ValidationResultList;
import com.helger.phive.api.source.IValidationSource;
import com.helger.phive.ves.model.v1.EVESSyntax;
import com.helger.phive.ves.model.v1.VES1Marshaller;
import com.helger.phive.ves.model.v1.VESStatus1Helper;
import com.helger.phive.ves.model.v1.VESStatus1Marshaller;
import com.helger.phive.ves.v10.VesCustomErrorType;
import com.helger.phive.ves.v10.VesErrorLevelType;
import com.helger.phive.ves.v10.VesNamespaceListType;
import com.helger.phive.ves.v10.VesNamespaceType;
import com.helger.phive.ves.v10.VesOutputType;
import com.helger.phive.ves.v10.VesRequiresType;
import com.helger.phive.ves.v10.VesResourceType;
import com.helger.phive.ves.v10.VesStatusType;
import com.helger.phive.ves.v10.VesType;
import com.helger.phive.xml.schematron.CustomErrorDetails;
import com.helger.xml.namespace.MapBasedNamespaceContext;
/**
* This class loads VES data into {@link LoadedVES} objects. It supports dynamic
* rule creation for each syntax, using the {@link IVESLoaderXSD},
* {@link IVESLoaderSchematron} and {@link IVESLoaderEdifact} classes. By
* default only XSD and Schematron are support. Edifact needs to be implemented
* manually.
*
* @author Philip Helger
*/
@ThreadSafe
public final class VESLoader
{
/**
* File extension for VES files
*/
public static final String FILE_EXT_VES = ".ves";
/**
* File extension for status files
*/
public static final String FILE_EXT_STATUS = ".status";
public static final boolean DEFAULT_USE_EAGER_REQUIREMENT_LOADING = false;
public static final boolean DEFAULT_RESOLVE_PSEUDO_VERSIONS = true;
private static final Logger LOGGER = LoggerFactory.getLogger (VESLoader.class);
private static final Duration DURATION_WARN = Duration.ofMillis (500);
private final SimpleReadWriteLock m_aRWLock = new SimpleReadWriteLock ();
private final IRepoStorageWithToc m_aRepo;
@GuardedBy ("m_aRWLock")
private IVESLoaderXSD m_aLoaderXSD = new DefaultVESLoaderXSD ();
@GuardedBy ("m_aRWLock")
private IVESLoaderSchematron m_aLoaderSchematron = new DefaultVESLoaderSchematron ();
@GuardedBy ("m_aRWLock")
private IVESLoaderEdifact m_aLoaderEdifact = null;
@GuardedBy ("m_aRWLock")
private boolean m_bUseEagerRequirementLoading = DEFAULT_USE_EAGER_REQUIREMENT_LOADING;
@GuardedBy ("m_aRWLock")
private boolean m_bResolvePseudoVersions = DEFAULT_RESOLVE_PSEUDO_VERSIONS;
public VESLoader (@Nonnull final IRepoStorageWithToc aRepo)
{
ValueEnforcer.notNull (aRepo, "Repo");
m_aRepo = aRepo;
}
/**
* @return The XSD loader to be used. May be null.
*/
@Nullable
public IVESLoaderXSD getLoaderXSD ()
{
return m_aRWLock.readLockedGet ( () -> m_aLoaderXSD);
}
/**
* Set the XSD loader to be used.
*
* @param aLoader
* The loader to be used. May be null.
* @return this for chaining
*/
@Nonnull
public VESLoader setLoaderXSD (@Nullable final IVESLoaderXSD aLoader)
{
m_aRWLock.writeLocked ( () -> m_aLoaderXSD = aLoader);
return this;
}
/**
* @return The Schematron loader to be used. May be null.
*/
@Nullable
public IVESLoaderSchematron getLoaderSchematron ()
{
return m_aRWLock.readLockedGet ( () -> m_aLoaderSchematron);
}
/**
* Set the Schematron loader to be used.
*
* @param aLoader
* The loader to be used. May be null.
* @return this for chaining
*/
@Nonnull
public VESLoader setLoaderSchematron (@Nullable final IVESLoaderSchematron aLoader)
{
m_aRWLock.writeLocked ( () -> m_aLoaderSchematron = aLoader);
return this;
}
/**
* @return The Edifact loader to be used. May be null.
*/
@Nullable
public IVESLoaderEdifact getLoaderEdifact ()
{
return m_aRWLock.readLockedGet ( () -> m_aLoaderEdifact);
}
/**
* Set the Edifact loader to be used.
*
* @param aLoader
* The loader to be used. May be null.
* @return this for chaining
*/
@Nonnull
public VESLoader setLoaderEdifact (@Nullable final IVESLoaderEdifact aLoader)
{
m_aRWLock.writeLocked ( () -> m_aLoaderEdifact = aLoader);
return this;
}
/**
* @return true if eager requirement loading is enabled,
* false if lazy loading is enabled.
*/
public boolean isUseEagerRequirementLoading ()
{
return m_aRWLock.readLockedBoolean ( () -> m_bUseEagerRequirementLoading);
}
/**
* User eager or lazy loading of dependent resources.
*
* @param b
* true for eager loading, false for lazy
* loading
* @return this for chaining
*/
@Nonnull
public VESLoader setUseEagerRequirementLoading (final boolean b)
{
m_aRWLock.writeLocked ( () -> m_bUseEagerRequirementLoading = b);
return this;
}
/**
* @return true if pseudo versions should be resolved,
* false otherwise. The default is
* {@link #DEFAULT_RESOLVE_PSEUDO_VERSIONS}
* @since 9.2.1
*/
@Nonnull
public boolean isResolvePseudoVersions ()
{
return m_aRWLock.readLockedBoolean ( () -> m_bResolvePseudoVersions);
}
/**
* Enable or disable the resolution of pseudo versions.
*
* @param b
* true to enable it, false to disable it
* @return this for chaining
* @since 9.2.1
*/
@Nonnull
public VESLoader setResolvePseudoVersions (@Nonnull final boolean b)
{
m_aRWLock.writeLocked ( () -> m_bResolvePseudoVersions = b);
return this;
}
@ThreadSafe
public static final class VESLoaderStatus
{
private final SimpleReadWriteLock m_aRWLock = new SimpleReadWriteLock ();
@GuardedBy ("m_aRWLock")
private final ICommonsSet m_aLoaded = new CommonsLinkedHashSet <> ();
@Nonnull
public ESuccess addVESID (@Nonnull final DVRCoordinate aVESID)
{
return ESuccess.valueOf (m_aRWLock.writeLockedBoolean ( () -> m_aLoaded.add (aVESID)));
}
@Nonnull
@Nonempty
public String getDepedencyChain (@Nullable final DVRCoordinate aLastOne)
{
final ICommonsList aList = m_aLoaded.getCopyAsList ();
if (aLastOne != null)
aList.add (aLastOne);
return StringHelper.imploder ().source (aList, x -> "'" + x.getAsSingleID () + "'").separator (" -> ").build ();
}
}
static void internalWrapNamespaceList (@Nullable final VesNamespaceListType aNamespaces,
@Nonnull final MapBasedNamespaceContext aNSCtx)
{
if (aNamespaces != null)
for (final VesNamespaceType aNamespace : aNamespaces.getNamespace ())
{
final String sPrefix = aNamespace.getPrefix ();
if (aNamespace.isOverwrite ())
{
// Simply overwrite
aNSCtx.addMapping (sPrefix, aNamespace.getUri ());
}
else
{
// Error in case it already exists
if (aNSCtx.getNamespaceURI (sPrefix).length () > 0)
LOGGER.error ("The namespace prefix '" + sPrefix + "' is already mapped in the current namespace context");
else
aNSCtx.addMapping (sPrefix, aNamespace.getUri ());
}
}
}
@Nonnull
private static MapBasedNamespaceContext _wrap (@Nullable final VesNamespaceListType aNamespaces)
{
final MapBasedNamespaceContext ret = new MapBasedNamespaceContext ();
internalWrapNamespaceList (aNamespaces, ret);
return ret;
}
@Nonnull
static EErrorLevel internalWrapErrorLevel (@Nonnull final VesErrorLevelType e)
{
switch (e)
{
case INFO:
return EErrorLevel.INFO;
case WARN:
return EErrorLevel.WARN;
case ERROR:
return EErrorLevel.ERROR;
default:
throw new IllegalStateException ("Unsupported error level " + e);
}
}
@Nonnull
private static LoadedVES.OutputType _wrap (@Nullable final VesOutputType aOutput)
{
final LoadedVES.OutputType ret = new LoadedVES.OutputType ();
if (aOutput != null)
for (final VesCustomErrorType aCustomError : aOutput.getCustomError ())
{
ret.addCustomErrorLevel (aCustomError.getId (),
new CustomErrorDetails (internalWrapErrorLevel (aCustomError.getLevel ()),
aCustomError.getErrorTextPrefix (),
aCustomError.getErrorTextSuffix ()));
}
return ret;
}
@Nullable
public LoadedVES convertToLoadedVES (@Nonnull final IValidationExecutorSetStatus aStatus,
@Nonnull final VesType aSrcVes,
@Nonnull final VESLoaderStatus aLoaderStatus,
@Nonnull final ErrorList aLoadingErrors) throws VESLoadingException
{
return _convertToLoadedVES (aStatus, null, aSrcVes, aLoaderStatus, aLoadingErrors);
}
@Nonnull
private static DVRCoordinate _createVESIDUnchecked (@Nonnull @Nonempty final String sGroupID,
@Nonnull @Nonempty final String sArtifactID,
@Nonnull @Nonempty final String sVersion)
{
try
{
return DVRCoordinate.create (sGroupID, sArtifactID, sVersion);
}
catch (final DVRVersionException ex)
{
throw new IllegalStateException (ex);
}
}
@Nullable
private LoadedVES _convertToLoadedVES (@Nonnull final IValidationExecutorSetStatus aStatus,
@Nullable final LoadedVES.RequiredVES aLoadingRequiredVES,
@Nonnull final VesType aSrcVes,
@Nonnull final VESLoaderStatus aLoaderStatus,
@Nonnull final ErrorList aLoadingErrors) throws VESLoadingException
{
ValueEnforcer.notNull (aStatus, "Status");
ValueEnforcer.notNull (aSrcVes, "VES");
ValueEnforcer.notNull (aLoaderStatus, "LoaderStatus");
ValueEnforcer.notNull (aLoadingErrors, "LoadingErrors");
final EVESSyntax eSyntax = aSrcVes.getXsd () != null ? EVESSyntax.XSD : !aSrcVes.getSchematron ().isEmpty ()
? EVESSyntax.SCHEMATRON
: EVESSyntax.EDIFACT;
// Extract data
final LoadedVES.Header aHeader = new LoadedVES.Header (_createVESIDUnchecked (aSrcVes.getGroupId (),
aSrcVes.getArtifactId (),
aSrcVes.getVersion ()),
aSrcVes.getName (),
aSrcVes.getReleased (),
eSyntax);
final LoadedVES ret = new LoadedVES (aHeader, aStatus);
if (aSrcVes.getRequires () != null)
{
final VesRequiresType aSrcReq = aSrcVes.getRequires ();
final LoadedVES.RequiredVES aSrcRequiredVES = new LoadedVES.RequiredVES (_createVESIDUnchecked (aSrcReq.getGroupId (),
aSrcReq.getArtifactId (),
aSrcReq.getVersion ()),
_wrap (aSrcReq.getNamespaces ()),
_wrap (aSrcReq.getOutput ()),
aSrcReq.isStopOnError ());
if (isUseEagerRequirementLoading ())
{
// Eager loading
// Recursive load required artefact; required to have this in scope
final LoadedVES aLoadedRequirement = _loadVESFromRepo (aSrcRequiredVES.getRequiredVESID (),
aSrcRequiredVES,
aLoaderStatus,
aLoadingErrors);
if (aLoadedRequirement == null)
{
aLoadingErrors.add (SingleError.builderError ()
.errorText ("Failed to load required VESID '" +
aSrcRequiredVES.getRequiredVESID ().getAsSingleID () +
"' [eager]")
.build ());
return null;
}
ret.setEagerRequires (aSrcRequiredVES, aLoadedRequirement);
}
else
{
// Lazy loading
ret.setLazyRequires (aSrcRequiredVES, aLocalErrorList -> {
// Use this deferred loader, to ensure the same surrounding VESLoader
// instance is used
// Recursive load required artefact; required to have this in scope
final LoadedVES aLoadedRequirement = _loadVESFromRepo (aSrcRequiredVES.getRequiredVESID (),
aSrcRequiredVES,
aLoaderStatus,
aLocalErrorList);
if (aLoadedRequirement == null)
throw new VESLoadingException ("Failed to load required VESID '" +
aSrcRequiredVES.getRequiredVESID ().getAsSingleID () +
"' [lazy]: " +
StringHelper.getImplodedMapped ("\n ",
aLocalErrorList,
IError::getAsStringLocaleIndepdent));
return aLoadedRequirement;
});
}
}
final IVESAsyncLoader aAsyncLoader = (aAsyncLoadVESID, sFileExt) -> {
// Always eager loading
final RepoStorageKeyOfArtefact aRepoKey = RepoStorageKeyOfArtefact.of (aAsyncLoadVESID, sFileExt);
return m_aRepo.read (aRepoKey);
};
if (aSrcVes.getXsd () != null)
{
// XSD
final IVESLoaderXSD aLoader = getLoaderXSD ();
if (aLoader != null)
{
ret.addExecutor (aLoader.loadXSD (m_aRepo,
aSrcVes.getXsd (),
aLoadingRequiredVES,
aLoadingErrors,
aAsyncLoader));
}
else
{
aLoadingErrors.add (SingleError.builderError ()
.errorText ("The VES contains an XSD element, but no XSD loader is present")
.build ());
}
}
else
if (!aSrcVes.getSchematron ().isEmpty ())
{
// Schematron
final IVESLoaderSchematron aLoader = getLoaderSchematron ();
if (aLoader != null)
{
ret.addExecutors (aLoader.loadSchematrons (m_aRepo,
aSrcVes.getSchematron (),
aLoadingRequiredVES,
aLoadingErrors,
aAsyncLoader));
}
else
{
aLoadingErrors.add (SingleError.builderError ()
.errorText ("The VES contains a Schematron element, but no Schematron loader is present")
.build ());
}
}
else
if (aSrcVes.getEdifact () != null)
{
// EDIFACT
final IVESLoaderEdifact aLoader = getLoaderEdifact ();
if (aLoader != null)
{
ret.addExecutor (aLoader.loadEdifact (m_aRepo,
aSrcVes.getEdifact (),
aLoadingRequiredVES,
aLoadingErrors,
aAsyncLoader));
}
else
{
aLoadingErrors.add (SingleError.builderError ()
.errorText ("The VES contains an Edifact element, but no Edifact loader is present")
.build ());
}
}
else
throw new IllegalStateException ("Unsupported base syntax");
if (!ret.hasExecutor ())
{
aLoadingErrors.add (SingleError.builderError ()
.errorText ("The loaded VES contains no Validation Executor and is therefore not usable")
.build ());
return null;
}
return ret;
}
@Nullable
public LoadedVES loadVESDirect (@Nonnull final IReadableResource aVESRes, @Nonnull final ErrorList aLoadingErrors)
{
return loadVESDirect (ValidationExecutorSetStatus.createValidNow (),
aVESRes,
new VESLoaderStatus (),
aLoadingErrors);
}
@Nullable
public LoadedVES loadVESDirect (@Nonnull final IValidationExecutorSetStatus aStatus,
@Nonnull final IReadableResource aVESRes,
@Nonnull final VESLoaderStatus aLoaderStatus,
@Nonnull final ErrorList aLoadingErrors)
{
ValueEnforcer.notNull (aStatus, "Status");
ValueEnforcer.notNull (aVESRes, "VESRes");
ValueEnforcer.notNull (aLoaderStatus, "LoaderStatus");
ValueEnforcer.notNull (aLoadingErrors, "LoadingErrors");
// Read VES as XML
final VesType aVES = new VES1Marshaller ().setCollectErrors (aLoadingErrors).read (aVESRes);
if (aVES == null)
{
// Error in XML
aLoadingErrors.add (SingleError.builderError ()
.errorFieldName (aVESRes.getPath ())
.errorText ("Failed to read VES as XML.")
.build ());
return null;
}
// Build VESID
final DVRCoordinate aVESID = _createVESIDUnchecked (aVES.getGroupId (), aVES.getArtifactId (), aVES.getVersion ());
LOGGER.info ("Trying to read VESID '" + aVESID.getAsSingleID () + "' directly");
// Ensure the VESID is not yet in the loader chain
if (aLoaderStatus.addVESID (aVESID).isFailure ())
{
aLoadingErrors.add (SingleError.builderError ()
.errorText ("The VESID '" +
aVESID.getAsSingleID () +
"' was already loaded. It seems like you have a circular dependency: " +
aLoaderStatus.getDepedencyChain (aVESID))
.build ());
return null;
}
// Convert to loaded VES
return convertToLoadedVES (aStatus, aVES, aLoaderStatus, aLoadingErrors);
}
/**
* Resolve the provided VESID with a pseudo version to a static version.
*
* @param aVESID
* The VESID with the pseudo version. May not be null.
* @param aVersionsToIgnore
* An optional set of static versions that should be ignored and not
* returned. May be null.
* @param aCheckDateTime
* The date and time for which the resolution should be performed. If
* null the current date and time will be used.
* @return null if the pseudo version could not be resolved.
* @since 9.2.1
*/
@Nullable
public DVRCoordinate resolvePseudoVersion (@Nonnull final DVRCoordinate aVESID,
@Nullable final Set aVersionsToIgnore,
@Nullable final OffsetDateTime aCheckDateTime)
{
return VESLoaderPseudoVersionResolver.resolvePseudoVersion (m_aRepo, aVESID, aVersionsToIgnore, aCheckDateTime);
}
/**
* Load a VES by the provided VESID and fill all errors into the provided
* {@link ErrorList}.
*
* @param aVESID
* The VESID to load. May not be null.
* @param aLoadingErrors
* The loading error list to be filled. May not be null.
* @return null if loading failed
*/
@Nullable
public LoadedVES loadVESFromRepo (@Nonnull final DVRCoordinate aVESID, @Nonnull final ErrorList aLoadingErrors)
{
return _loadVESFromRepo (aVESID, null, new VESLoaderStatus (), aLoadingErrors);
}
/**
* Load a VES by the provided VESID and fill all errors into the provided
* {@link ErrorList}.
*
* @param aVESID
* The VESID to load. May not be null.
* @param aLoaderStatus
* The internal loader status to be used, to make sure no cycles etc.
* are contained
* @param aLoadingErrors
* The loading error list to be filled. May not be null.
* @return null if loading failed
*/
@Nullable
public LoadedVES loadVESFromRepo (@Nonnull final DVRCoordinate aVESID,
@Nonnull final VESLoaderStatus aLoaderStatus,
@Nonnull final ErrorList aLoadingErrors)
{
return _loadVESFromRepo (aVESID, null, aLoaderStatus, aLoadingErrors);
}
/**
* Load a VES by the provided VESID and fill all errors into the provided
* {@link ErrorList}.
*
* @param aVESID
* The VESID to load. May be a static version or a pseudo version. May
* not be null.
* @param aLoadingRequiredVES
* The required VES that is currently loaded. May be null.
* @param aLoaderStatus
* The internal loader status to be used, to make sure no cycles etc.
* are contained
* @param aLoadingErrors
* The loading error list to be filled. May not be null.
* @return null if loading failed
*/
@Nullable
private LoadedVES _loadVESFromRepo (@Nonnull final DVRCoordinate aVESID,
@Nullable final LoadedVES.RequiredVES aLoadingRequiredVES,
@Nonnull final VESLoaderStatus aLoaderStatus,
@Nonnull final ErrorList aLoadingErrors)
{
ValueEnforcer.notNull (aVESID, "VESID");
ValueEnforcer.notNull (aLoaderStatus, "LoaderStatus");
ValueEnforcer.notNull (aLoadingErrors, "LoadingErrors");
final boolean bIsRoot = aLoaderStatus.m_aLoaded.isEmpty ();
// resolve eventual pseudo version here
final DVRCoordinate aStaticVESID;
if (aVESID.getVersionObj ().isStaticVersion ())
aStaticVESID = aVESID;
else
{
if (!isResolvePseudoVersions ())
{
LOGGER.error ("Failed to resolve pseudo version in '" +
aVESID.getAsSingleID () +
"' as pseudo version resolution is disabled");
return null;
}
aStaticVESID = resolvePseudoVersion (aVESID, null, null);
if (aStaticVESID == null)
{
LOGGER.error ("Failed to resolve pseudo version in '" + aVESID.getAsSingleID () + "'");
return null;
}
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Successfully resolved pseudo version in '" +
aVESID.getAsSingleID () +
"' to '" +
aStaticVESID.getAsSingleID () +
"'");
}
LOGGER.info ("Trying to read VESID " + aLoaderStatus.getDepedencyChain (aStaticVESID) + " from repository");
LoadedVES ret = null;
try
{
// Ensure the VESID is not yet in the loader chain
if (aLoaderStatus.addVESID (aStaticVESID).isFailure ())
{
// This is a circular dependency
aLoadingErrors.add (SingleError.builderError ()
.errorText ("The VESID '" +
aStaticVESID.getAsSingleID () +
"' was already loaded. It seems like you have a circular dependency: " +
aLoaderStatus.getDepedencyChain (aStaticVESID))
.build ());
return null;
}
// Check if an explicit status is available
final IValidationExecutorSetStatus aStatus;
final RepoStorageKeyOfArtefact aRepoKeyStatus = RepoStorageKeyOfArtefact.of (aStaticVESID, FILE_EXT_STATUS);
if (m_aRepo.exists (aRepoKeyStatus))
{
final IRepoStorageReadItem aRepoContentStatus = m_aRepo.read (aRepoKeyStatus);
// it's okay, if it does not exist
if (aRepoContentStatus != null)
{
// Read VES Status as XML
final VesStatusType aVESStatus = new VESStatus1Marshaller ().setCollectErrors (aLoadingErrors)
.read (new RepoStorageReadableResource (aRepoKeyStatus,
aRepoContentStatus.getContent ()));
if (aVESStatus == null)
{
// Error in Status XML - breaking error
aLoadingErrors.add (SingleError.builderError ()
.errorFieldName (aRepoKeyStatus.getPath ())
.errorText ("Failed to read VES Status as XML.")
.build ());
return null;
}
// Convert JAXB data model to Java date model
aStatus = VESStatus1Helper.convert (aVESStatus);
}
else
{
// Status is supposed to exist, but does not
aStatus = ValidationExecutorSetStatus.createValidNow ();
}
}
else
{
// Status does not exist
aStatus = ValidationExecutorSetStatus.createValidNow ();
}
// Read VES content from repo
final RepoStorageKeyOfArtefact aRepoKeyVES = RepoStorageKeyOfArtefact.of (aStaticVESID, FILE_EXT_VES);
final IRepoStorageReadItem aRepoContentVES = m_aRepo.read (aRepoKeyVES);
if (aRepoContentVES == null)
{
aLoadingErrors.add (SingleError.builderError ()
.errorFieldName (aRepoKeyVES.getPath ())
.errorText ("Failed to resolve provided VES from repository.")
.build ());
return null;
}
// Read VES as XML
final VesType aVES = new VES1Marshaller ().setCollectErrors (aLoadingErrors)
.read (new RepoStorageReadableResource (aRepoKeyVES,
aRepoContentVES.getContent ()));
if (aVES == null)
{
// Error in XML
aLoadingErrors.add (SingleError.builderError ()
.errorFieldName (aRepoKeyVES.getPath ())
.errorText ("Failed to read VES as XML.")
.build ());
return null;
}
// Now read into data model
ret = _convertToLoadedVES (aStatus, aLoadingRequiredVES, aVES, aLoaderStatus, aLoadingErrors);
return ret;
}
finally
{
if (bIsRoot)
if (ret == null)
LOGGER.error ("Failed to load VESID '" + aStaticVESID.getAsSingleID () + "' from repository");
else
LOGGER.info ("Successfully finished loading VESID '" + aStaticVESID.getAsSingleID () + "' from repository");
}
}
@Nonnull
static RepoStorageKeyOfArtefact createRepoStorageKey (@Nonnull final VesResourceType aVRT)
{
// File extension must start with a dot
return RepoStorageKeyOfArtefact.of (_createVESIDUnchecked (aVRT.getGroupId (),
aVRT.getArtifactId (),
aVRT.getVersion ()), "." + aVRT.getType ());
}
/**
* Load the validation rules from an external repository, identified by a
* VESID and apply the validation rules onto the provided data to be
* validated. All errors occurring are stored in the provided error list.
*
* @param aRepo
* Repository to load from. Must not be null.
* @param aVESID
* The VESID of the artefacts to load. Must not be null.
* @param aValidationSource
* The data to be validated. Must not be null.
* @param aLoadingErrors
* The loading error list to be filled. Contains only the loading
* errors - validation errors will be returned in a separate
* {@link ValidationResultList}. Must not be null.
* @return The validation result
* @throws VESLoadingException
* If anything goes wrong
*/
@Nonnull
public static VESValidationResult loadVESAndApplyValidation (@Nonnull final IRepoStorageWithToc aRepo,
@Nonnull final DVRCoordinate aVESID,
@Nonnull final IValidationSource aValidationSource,
@Nonnull final ErrorList aLoadingErrors) throws VESLoadingException
{
ValueEnforcer.notNull (aRepo, "RepoChain");
ValueEnforcer.notNull (aVESID, "VESID");
ValueEnforcer.notNull (aValidationSource, "ValidationSource");
ValueEnforcer.notNull (aLoadingErrors, "LoadingErrors");
// Remember start date time
final OffsetDateTime aStartDT = PDTFactory.getCurrentOffsetDateTime ();
// load
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Start VES loading of '" + aVESID.getAsSingleID () + "'");
final StopWatch aSWLoad = StopWatch.createdStarted ();
final LoadedVES aLoadedVES = new VESLoader (aRepo).setUseEagerRequirementLoading (true)
.loadVESFromRepo (aVESID, aLoadingErrors);
aSWLoad.stop ();
if (aLoadedVES == null)
{
throw new VESLoadingException ("Failed to load VES '" +
aVESID.getAsSingleID () +
"': " +
StringHelper.getImplodedMapped ("\n ",
aLoadingErrors,
IError::getAsStringLocaleIndepdent));
}
final Duration aLoadDuration = aSWLoad.getDuration ();
if (aLoadDuration.compareTo (DURATION_WARN) > 0)
LOGGER.warn ("Finished VES loading of '" + aVESID.getAsSingleID () + "' after " + aLoadDuration);
// validate
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Start validation of '" + aVESID.getAsSingleID () + "'");
final ValidationResultList aValidationResultList = new ValidationResultList ();
final Duration aValidateDuration = StopWatch.runMeasured ( () -> {
aLoadedVES.applyValidation (aValidationSource, aValidationResultList, Locale.ENGLISH);
});
if (aValidateDuration.compareTo (DURATION_WARN) > 0)
LOGGER.warn ("Finished validation of '" + aVESID.getAsSingleID () + "' after " + aValidateDuration);
// Collect all results
return new VESValidationResult (aVESID,
aValidationSource,
aStartDT,
aLoadDuration,
aValidateDuration,
aValidationResultList);
}
}