
ai.h2o.mojos.runtime.api.MojoPipelineService Maven / Gradle / Ivy
package ai.h2o.mojos.runtime.api;
import ai.h2o.mojos.runtime.MojoPipeline;
import ai.h2o.mojos.runtime.api.backend.DirReaderBackend;
import ai.h2o.mojos.runtime.api.backend.ReaderBackend;
import ai.h2o.mojos.runtime.api.backend.ZipFileReaderBackend;
import ai.h2o.mojos.runtime.lic.LicenseException;
import ai.h2o.mojos.runtime.utils.ClassLoaderUtils;
import ai.h2o.mojos.runtime.utils.Consts;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.ServiceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Primary pipeline service.
*
* This class takes care of instantiating pipeline based on what's found in a given backend.
* And, because one backed can contain multiple pipeline formats, the choice can be configured with property sys.ai.h2o.mojos.pipelineFormats
.
* It contains a comma separated list of format provider names, which are defined by their {@link PipelineLoaderFactory#getName()} method.
*/
public class MojoPipelineService {
private static final Logger log = LoggerFactory.getLogger(MojoPipelineService.class);
private final LinkedHashMap registry = new LinkedHashMap<>();
private MojoPipelineService(String... pipelineFormats) {
// gather all providers
final LinkedHashMap factories = new LinkedHashMap<>();
final ServiceLoader loader = ServiceLoader.load(
PipelineLoaderFactory.class,
ClassLoaderUtils.getPreferredSpiClassLoader(PipelineLoaderFactory.class));
for (PipelineLoaderFactory factory : loader) {
final String name = factory.getName();
final PipelineLoaderFactory existing = factories.put(name, factory);
if (existing != null) {
throw new IllegalStateException(String.format("Pipeline loader '%s' is already registered with class '%s'", name, existing.getClass().getName()));
}
}
// first, register preferred format providers in the order of preference
for (String pipelineFormat : pipelineFormats) {
final PipelineLoaderFactory provider = factories.remove(pipelineFormat);
if (provider != null) {
registry.put(pipelineFormat, provider);
} else {
log.warn("No pipeline format provider for '{}'", pipelineFormat);
}
}
// then add the rest
registry.putAll(factories);
}
public PipelineLoaderFactory get(ReaderBackend backend) throws IOException {
if (registry.isEmpty()) {
throw new IllegalStateException("No pipeline factory is available");
}
for (Map.Entry entry : registry.entrySet()) {
final PipelineLoaderFactory factory = entry.getValue();
final String rootResource = factory.getRootResource();
if (backend.exists(rootResource)) {
return factory;
}
}
throw new IOException(String.format("None of %d available pipeline factories %s can read this mojo.", registry.size(), registry.keySet()));
}
/**
* The global service to provide pipeline service.
* @see MojoPipelineService
*/
public static MojoPipelineService INSTANCE = new MojoPipelineService(Consts.getSysProp("pipelineFormats", "pbuf,toml,klime,h2o3").split(","));
private static ReaderBackend autodetectBackend(File file) throws IOException {
if (!file.exists()) {
throw new FileNotFoundException(file.getAbsolutePath());
} else if (file.isDirectory()) {
return DirReaderBackend.open(file);
} else if (file.isFile()) {
return ZipFileReaderBackend.open(file);
} else {
throw new IOException("Unsupported file type: " + file.getAbsolutePath());
}
}
/**
* Loads {@link MojoPipeline pipeline} from a file.
* @param file the file or directory containing pipeline resources
* @param config -
* @return pipeline
*/
public static MojoPipeline loadPipeline(File file, final PipelineConfig config) throws IOException, LicenseException {
final ReaderBackend backend = autodetectBackend(file);
return loadPipeline(backend, config);
}
/**
* @deprecated use {@link #loadPipeline(File, PipelineConfig)} instead
*/
@Deprecated
public static MojoPipeline loadPipeline(File file) throws IOException, LicenseException {
return loadPipeline(file, PipelineConfig.DEFAULT);
}
/**
* Loads {@link MojoPipeline pipeline} from a backend.
* @param backend the backend providing access to pipeline resources
* @return pipeline
*/
public static MojoPipeline loadPipeline(ReaderBackend backend, final PipelineConfig config) throws IOException, LicenseException {
final PipelineLoaderFactory loaderFactory = INSTANCE.get(backend);
final PipelineLoader loader = loaderFactory.createLoader(backend, null, config);
try {
// This check is to ensure that h2o implementations are always protected against simple overriding (and avoiding AccessManager check)
// It will fail only in our CI; customer will never experience it.
final Method method = loader.getClass().getMethod("load");
final int modifiers = method.getModifiers();
if (!Modifier.isFinal(modifiers)) {
throw new IllegalStateException(String.format("Internal error: Method %s#%s() is required to be declared final", loader.getClass().getName(), method.getName()));
}
// In future, we might also want detect if AccessManager was used during call to load.
return loader.load();
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e);
}
}
/**
* @deprecated use {@link #loadPipeline(ReaderBackend, PipelineConfig)} instead
*/
@Deprecated
public static MojoPipeline loadPipeline(ReaderBackend backend) throws IOException, LicenseException {
return loadPipeline(backend, PipelineConfig.DEFAULT);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy