/*
* Copyright © 2014-2015 Cask Data, Inc.
*
* 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 co.cask.cdap.internal.app.program;
import co.cask.cdap.api.app.ApplicationSpecification;
import co.cask.cdap.app.program.ManifestFields;
import co.cask.cdap.archive.ArchiveBundler;
import co.cask.cdap.internal.app.ApplicationSpecificationAdapter;
import co.cask.cdap.internal.io.ReflectionSchemaGenerator;
import co.cask.cdap.proto.Id;
import com.google.common.base.Charsets;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.InputSupplier;
import org.apache.twill.filesystem.Location;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.Manifest;
import javax.annotation.Nullable;
/**
*
*/
public final class ProgramBundle {
private static final String APPLICATION_META_ENTRY = "application.json";
private static final Predicate<JarEntry> META_IGNORE = new Predicate<JarEntry>() {
@Override
public boolean apply(JarEntry input) {
return input.getName().contains("MANIFEST.MF") || input.getName().contains(APPLICATION_META_ENTRY);
}
};
/**
* Clones a give application archive using the {@link co.cask.cdap.archive.ArchiveBundler}.
* A new manifest file will be amended to the jar.
*
* @return An instance of {@link Location} containing the program JAR.
*
* @throws java.io.IOException in case of any issue related to copying jars.
*/
public static Location create(Id.Program programId, ArchiveBundler bundler, Location output,
String className, ApplicationSpecification appSpec) throws IOException {
return create(programId, bundler, output, className, appSpec, null);
}
public static Location create(Id.Program programId, ArchiveBundler bundler, Location output, String className,
ApplicationSpecification appSpec, @Nullable Manifest other) throws IOException {
// Create a MANIFEST file
Manifest manifest = new Manifest();
// Copy over attributes from other manifest
if (other != null) {
for (Map.Entry<Object, Object> entry : other.getMainAttributes().entrySet()) {
Attributes.Name key = (Attributes.Name) entry.getKey();
String value = (String) entry.getValue();
manifest.getMainAttributes().put(key, value);
}
}
manifest.getMainAttributes().put(ManifestFields.MANIFEST_VERSION, ManifestFields.VERSION);
manifest.getMainAttributes().put(ManifestFields.MAIN_CLASS, className);
manifest.getMainAttributes().put(ManifestFields.SPEC_FILE, ManifestFields.MANIFEST_SPEC_FILE);
manifest.getMainAttributes().put(ManifestFields.ACCOUNT_ID, programId.getNamespaceId());
manifest.getMainAttributes().put(ManifestFields.APPLICATION_ID, programId.getApplicationId());
manifest.getMainAttributes().put(ManifestFields.PROGRAM_NAME, programId.getId());
manifest.getMainAttributes().put(ManifestFields.PROGRAM_TYPE, programId.getType().name());
bundler.clone(output, manifest, ImmutableMap.of(APPLICATION_META_ENTRY, getInputSupplier(appSpec)), META_IGNORE);
return output;
}
private static InputSupplier<InputStream> getInputSupplier(final ApplicationSpecification appSpec) {
return new InputSupplier<InputStream>() {
@Override
public InputStream getInput() throws IOException {
String json = ApplicationSpecificationAdapter.create(new ReflectionSchemaGenerator()).toJson(appSpec);
return new ByteArrayInputStream(json.getBytes(Charsets.UTF_8));
}
};
}
}