use of io.cdap.cdap.api.app.Application in project cdap by caskdata.
the class ApplicationLifecycleService method updateApplicationInternal.
/**
* Updates an application config by applying given update actions. The app should know how to apply these actions
* to its config.
*/
private void updateApplicationInternal(ApplicationId appId, @Nullable String currentConfigStr, ProgramTerminator programTerminator, ArtifactDetail artifactDetail, List<ApplicationConfigUpdateAction> updateActions, Set<ArtifactScope> allowedArtifactScopes, boolean allowSnapshot, @Nullable KerberosPrincipalId ownerPrincipal, boolean updateSchedules) throws Exception {
ApplicationClass appClass = Iterables.getFirst(artifactDetail.getMeta().getClasses().getApps(), null);
if (appClass == null) {
// This should never happen.
throw new IllegalStateException(String.format("No application class found in artifact '%s' in namespace '%s'.", artifactDetail.getDescriptor().getArtifactId(), appId.getParent()));
}
io.cdap.cdap.proto.id.ArtifactId artifactId = Artifacts.toProtoArtifactId(appId.getParent(), artifactDetail.getDescriptor().getArtifactId());
EntityImpersonator classLoaderImpersonator = new EntityImpersonator(artifactId, this.impersonator);
String updatedAppConfig;
DefaultApplicationUpdateContext updateContext = new DefaultApplicationUpdateContext(appId.getParent(), appId, artifactDetail.getDescriptor().getArtifactId(), artifactRepository, currentConfigStr, updateActions, allowedArtifactScopes, allowSnapshot);
try (CloseableClassLoader artifactClassLoader = artifactRepository.createArtifactClassLoader(artifactDetail.getDescriptor(), classLoaderImpersonator)) {
Object appMain = artifactClassLoader.loadClass(appClass.getClassName()).newInstance();
// Run config update logic for the application to generate updated config.
if (!(appMain instanceof Application)) {
throw new IllegalStateException(String.format("Application main class is of invalid type: %s", appMain.getClass().getName()));
}
Application app = (Application) appMain;
Type configType = Artifacts.getConfigType(app.getClass());
if (!app.isUpdateSupported()) {
String errorMessage = String.format("Application %s does not support update.", appId);
throw new UnsupportedOperationException(errorMessage);
}
ApplicationUpdateResult<?> updateResult = app.updateConfig(updateContext);
updatedAppConfig = GSON.toJson(updateResult.getNewConfig(), configType);
}
// Deploy application with with potentially new app config and new artifact.
AppDeploymentInfo deploymentInfo = new AppDeploymentInfo(artifactId, artifactDetail.getDescriptor().getLocation(), appId.getParent(), appClass, appId.getApplication(), appId.getVersion(), updatedAppConfig, ownerPrincipal, updateSchedules, null);
Manager<AppDeploymentInfo, ApplicationWithPrograms> manager = managerFactory.create(programTerminator);
// TODO: (CDAP-3258) Manager needs MUCH better error handling.
ApplicationWithPrograms applicationWithPrograms;
try {
applicationWithPrograms = manager.deploy(deploymentInfo).get();
} catch (ExecutionException e) {
Throwables.propagateIfPossible(e.getCause(), Exception.class);
throw Throwables.propagate(e.getCause());
}
adminEventPublisher.publishAppCreation(applicationWithPrograms.getApplicationId(), applicationWithPrograms.getSpecification());
}
use of io.cdap.cdap.api.app.Application in project cdap by caskdata.
the class DefaultArtifactInspector method inspectApplications.
private ArtifactClasses.Builder inspectApplications(Id.Artifact artifactId, ArtifactClasses.Builder builder, Location artifactLocation, ClassLoader artifactClassLoader) throws IOException, InvalidArtifactException {
// right now we force users to include the application main class as an attribute in their manifest,
// which forces them to have a single application class.
// in the future, we may want to let users do this or maybe specify a list of classes or
// a package that will be searched for applications, to allow multiple applications in a single artifact.
String mainClassName;
try {
Manifest manifest = BundleJarUtil.getManifest(artifactLocation);
if (manifest == null) {
return builder;
}
Attributes manifestAttributes = manifest.getMainAttributes();
if (manifestAttributes == null) {
return builder;
}
mainClassName = manifestAttributes.getValue(ManifestFields.MAIN_CLASS);
} catch (ZipException e) {
throw new InvalidArtifactException(String.format("Couldn't unzip artifact %s, please check it is a valid jar file.", artifactId), e);
}
if (mainClassName == null) {
return builder;
}
try {
Class<?> mainClass = artifactClassLoader.loadClass(mainClassName);
if (!(Application.class.isAssignableFrom(mainClass))) {
// possible for 3rd party plugin artifacts to have the main class set
return builder;
}
Application app = (Application) mainClass.newInstance();
java.lang.reflect.Type configType;
// we can deserialize the config into that object. Otherwise it'll just be a Config
try {
configType = Artifacts.getConfigType(app.getClass());
} catch (Exception e) {
throw new InvalidArtifactException(String.format("Could not resolve config type for Application class %s in artifact %s. " + "The type must extend Config and cannot be parameterized.", mainClassName, artifactId));
}
Schema configSchema = configType == Config.class ? null : schemaGenerator.generate(configType);
builder.addApp(new ApplicationClass(mainClassName, "", configSchema, getArtifactRequirements(app.getClass())));
} catch (ClassNotFoundException e) {
throw new InvalidArtifactException(String.format("Could not find Application main class %s in artifact %s.", mainClassName, artifactId));
} catch (UnsupportedTypeException e) {
throw new InvalidArtifactException(String.format("Config for Application %s in artifact %s has an unsupported schema. " + "The type must extend Config and cannot be parameterized.", mainClassName, artifactId));
} catch (InstantiationException | IllegalAccessException e) {
throw new InvalidArtifactException(String.format("Could not instantiate Application class %s in artifact %s.", mainClassName, artifactId), e);
}
return builder;
}
use of io.cdap.cdap.api.app.Application in project cdap by caskdata.
the class IntegrationTestManager method deployApplication.
@Override
@SuppressWarnings("unchecked")
public ApplicationManager deployApplication(NamespaceId namespace, Class<? extends Application> applicationClz, @Nullable Config configObject, File... bundleEmbeddedJars) {
// See if the application class comes from file or jar.
// If it's from JAR, no need to trace dependency since it should already be in an application jar.
URL appClassURL = applicationClz.getClassLoader().getResource(applicationClz.getName().replace('.', '/') + ".class");
// Should never happen, otherwise the ClassLoader is broken
Preconditions.checkNotNull(appClassURL, "Cannot find class %s from the classloader", applicationClz);
String appConfig = "";
Type configType = Artifacts.getConfigType(applicationClz);
RemoteApplicationManager manager;
try {
if (configObject != null) {
appConfig = GSON.toJson(configObject);
} else {
configObject = (Config) TypeToken.of(configType).getRawType().newInstance();
}
// Create and deploy application jar
File appJarFile = new File(tmpFolder, String.format("%s-%s.jar", applicationClz.getSimpleName(), VERSION));
try {
if ("jar".equals(appClassURL.getProtocol())) {
copyJarFile(appClassURL, appJarFile);
} else {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(ManifestFields.BUNDLE_VERSION, VERSION);
Location appJar = AppJarHelper.createDeploymentJar(locationFactory, applicationClz, manifest, CLASS_ACCEPTOR, bundleEmbeddedJars);
try (InputStream input = appJar.getInputStream()) {
Files.copy(input, appJarFile.toPath());
}
}
// Extracts application id from the application class
Application application = applicationClz.newInstance();
MockAppConfigurer configurer = new MockAppConfigurer(application);
application.configure(configurer, new DefaultApplicationContext<>(configObject));
String applicationId = configurer.getName();
// Upload artifact for application
ContentProvider<InputStream> artifactStream = locationFactory.create(appJarFile.toURI())::getInputStream;
artifactClient.add(namespace, applicationClz.getSimpleName(), artifactStream, VERSION);
List<ArtifactSummary> deployedArtifacts = artifactClient.list(namespace);
assert deployedArtifacts.size() > 0;
// Deploy application
ArtifactSummary summary = new ArtifactSummary(applicationClz.getSimpleName(), VERSION);
AppRequest<?> request = new AppRequest<>(summary, appConfig);
ApplicationId id = namespace.app(applicationId);
applicationClient.deploy(id, request);
manager = new RemoteApplicationManager(namespace.app(applicationId), clientConfig, restClient);
} finally {
if (!appJarFile.delete()) {
LOG.warn("Failed to delete temporary app jar {}", appJarFile.getAbsolutePath());
}
}
return manager;
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
use of io.cdap.cdap.api.app.Application in project cdap by caskdata.
the class UnitTestManager method deployApplication.
@Override
public ApplicationManager deployApplication(NamespaceId namespace, Class<? extends Application> applicationClz, @Nullable Config configObject, File... bundleEmbeddedJars) throws AccessException {
Preconditions.checkNotNull(applicationClz, "Application class cannot be null.");
Type configType = Artifacts.getConfigType(applicationClz);
try {
ArtifactId artifactId = new ArtifactId(namespace.getNamespace(), applicationClz.getSimpleName(), "1.0-SNAPSHOT");
addAppArtifact(artifactId, applicationClz, new Manifest(), bundleEmbeddedJars);
if (configObject == null) {
configObject = (Config) TypeToken.of(configType).getRawType().newInstance();
}
Application app = applicationClz.newInstance();
MockAppConfigurer configurer = new MockAppConfigurer(app);
app.configure(configurer, new DefaultApplicationContext<>(configObject));
ApplicationId applicationId = new ApplicationId(namespace.getNamespace(), configurer.getName());
ArtifactSummary artifactSummary = new ArtifactSummary(artifactId.getArtifact(), artifactId.getVersion());
appFabricClient.deployApplication(Id.Application.fromEntityId(applicationId), new AppRequest(artifactSummary, configObject));
return appManagerFactory.create(applicationId);
} catch (AccessException e) {
throw e;
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
use of io.cdap.cdap.api.app.Application in project cdap by caskdata.
the class ApplicationLifecycleService method getLatestAppArtifactForUpgrade.
/**
* Finds latest application artifact for given application and current artifact for upgrading application.
* If no artifact found then returns current artifact as the candidate.
*
* @param appId application Id to find latest app artifact for.
* @param currentArtifactId current artifact used by application.
* @param allowedArtifactScopes artifact scopes to search in for finding candidate artifacts.
* @param allowSnapshot whether to consider snapshot version of artifacts or not for upgrade.
* @return {@link ArtifactSummary} for the artifact to be used for upgrade purpose.
* @throws NotFoundException if there is no artifact available for given artifact.
* @throws Exception if there was an exception during finding candidate artifact.
*/
private ArtifactSummary getLatestAppArtifactForUpgrade(ApplicationId appId, ArtifactId currentArtifactId, Set<ArtifactScope> allowedArtifactScopes, boolean allowSnapshot) throws Exception {
List<ArtifactSummary> availableArtifacts = new ArrayList<>();
// At the least, current artifact should be in the set of available artifacts.
availableArtifacts.add(ArtifactSummary.from(currentArtifactId));
// Find candidate artifacts from all scopes we need to consider.
for (ArtifactScope scope : allowedArtifactScopes) {
NamespaceId artifactNamespaceToConsider = ArtifactScope.SYSTEM.equals(scope) ? NamespaceId.SYSTEM : appId.getParent();
List<ArtifactSummary> artifacts;
try {
artifacts = artifactRepository.getArtifactSummaries(artifactNamespaceToConsider, currentArtifactId.getName(), Integer.MAX_VALUE, ArtifactSortOrder.ASC);
} catch (ArtifactNotFoundException e) {
// This can happen if we are looking for candidate artifact in multiple namespace.
continue;
}
for (ArtifactSummary artifactSummary : artifacts) {
ArtifactVersion artifactVersion = new ArtifactVersion(artifactSummary.getVersion());
// Consider if it is a non-snapshot version artifact or it is a snapshot version than allowSnapshot is true.
if ((artifactVersion.isSnapshot() && allowSnapshot) || !artifactVersion.isSnapshot()) {
availableArtifacts.add(artifactSummary);
}
}
}
// Find the artifact with latest version.
Optional<ArtifactSummary> newArtifactCandidate = availableArtifacts.stream().max(Comparator.comparing(artifactSummary -> new ArtifactVersion(artifactSummary.getVersion())));
io.cdap.cdap.proto.id.ArtifactId currentArtifact = new io.cdap.cdap.proto.id.ArtifactId(appId.getNamespace(), currentArtifactId.getName(), currentArtifactId.getVersion().getVersion());
return newArtifactCandidate.orElseThrow(() -> new ArtifactNotFoundException(currentArtifact));
}
Aggregations