You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by si...@apache.org on 2019/05/15 22:23:26 UTC
[sling-org-apache-sling-feature-cpconverter] branch master updated:
multiple conversion logic moved from the CLI wrapper to the core converter
VaultPackaging reading operation optimized by opening them only once.
This is an automated email from the ASF dual-hosted git repository.
simonetripodi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-cpconverter.git
The following commit(s) were added to refs/heads/master by this push:
new 817e4d9 multiple conversion logic moved from the CLI wrapper to the core converter VaultPackaging reading operation optimized by opening them only once.
817e4d9 is described below
commit 817e4d93067de99efdd64f7e6c503e7d8be4f4b7
Author: Simo Tripodi <st...@adobe.com>
AuthorDate: Thu May 16 00:23:19 2019 +0200
multiple conversion logic moved from the CLI wrapper to the core
converter
VaultPackaging reading operation optimized by opening them only once.
---
.../ContentPackage2FeatureModelConverter.java | 175 ++++++++++++++-------
...ntentPackage2FeatureModelConverterLauncher.java | 71 +--------
.../ContentPackage2FeatureModelConverterTest.java | 57 ++++++-
...tPackage2FeatureModelConverterLauncherTest.java | 77 ---------
.../feature/cpconverter/{cli => }/test_a-1.0.zip | Bin
.../feature/cpconverter/{cli => }/test_b-1.0.zip | Bin
.../feature/cpconverter/{cli => }/test_c-1.0.zip | Bin
.../feature/cpconverter/{cli => }/test_d-1.0.zip | Bin
.../feature/cpconverter/{cli => }/test_e-1.0.zip | Bin
9 files changed, 176 insertions(+), 204 deletions(-)
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
index 678fcf4..6c2d81e 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverter.java
@@ -19,9 +19,19 @@ package org.apache.sling.feature.cpconverter;
import static java.util.Objects.requireNonNull;
import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
import org.apache.jackrabbit.vault.fs.io.Archive;
import org.apache.jackrabbit.vault.fs.io.Archive.Entry;
+import org.apache.jackrabbit.vault.packaging.CyclicDependencyException;
+import org.apache.jackrabbit.vault.packaging.Dependency;
+import org.apache.jackrabbit.vault.packaging.PackageId;
import org.apache.jackrabbit.vault.packaging.PackageManager;
import org.apache.jackrabbit.vault.packaging.PackageProperties;
import org.apache.jackrabbit.vault.packaging.VaultPackage;
@@ -111,84 +121,135 @@ public class ContentPackage2FeatureModelConverter extends BaseVaultPackageScanne
return mainPackageAssembler;
}
- public void convert(File contentPackage) throws Exception {
- requireNonNull(contentPackage , "Null content-package can not be converted.");
+ public void convert(File...contentPackages) throws Exception {
+ requireNonNull(contentPackages , "Null content-package(s) can not be converted.");
- if (!contentPackage.exists() || !contentPackage.isFile()) {
- throw new IllegalArgumentException("Content-package "
- + contentPackage
- + " does not exist or it is not a valid file.");
- }
+ logger.info("Ordering input content-package(s) {}...", Arrays.toString(contentPackages));
- logger.info("Reading content-package '{}'...", contentPackage);
+ Collection<VaultPackage> orderedContentPackages = firstPass(contentPackages);
- try (VaultPackage vaultPackage = packageManager.open(contentPackage, isStrictValidation())) {
- logger.info("content-package '{}' successfully read!", contentPackage);
+ logger.info("New content-package(s) order: {}", orderedContentPackages);
- mainPackageAssembler = VaultPackageAssembler.create(vaultPackage);
- PackageProperties packageProperties = vaultPackage.getProperties();
-
- String group = requireNonNull(packageProperties.getProperty(PackageProperties.NAME_GROUP),
- PackageProperties.NAME_GROUP
- + " property not found in content-package "
- + contentPackage
- + ", please check META-INF/vault/properties.xml")
- .replace('/', '.');
-
- String name = requireNonNull(packageProperties.getProperty(PackageProperties.NAME_NAME),
- PackageProperties.NAME_NAME
- + " property not found in content-package "
- + contentPackage
- + ", please check META-INF/vault/properties.xml");
-
- String version = packageProperties.getProperty(PackageProperties.NAME_VERSION);
- if (version == null || version.isEmpty()) {
- version = DEFEAULT_VERSION;
- }
+ for (VaultPackage vaultPackage : orderedContentPackages) {
+ try {
+ mainPackageAssembler = VaultPackageAssembler.create(vaultPackage);
+ PackageProperties packageProperties = vaultPackage.getProperties();
- String description = packageProperties.getDescription();
+ String group = requireNonNull(packageProperties.getProperty(PackageProperties.NAME_GROUP),
+ PackageProperties.NAME_GROUP
+ + " property not found in content-package "
+ + vaultPackage
+ + ", please check META-INF/vault/properties.xml")
+ .replace('/', '.');
- featuresManager.init(group,
- name,
- version,
- description);
+ String name = requireNonNull(packageProperties.getProperty(PackageProperties.NAME_NAME),
+ PackageProperties.NAME_NAME
+ + " property not found in content-package "
+ + vaultPackage
+ + ", please check META-INF/vault/properties.xml");
- logger.info("Converting content-package '{}'...", vaultPackage.getId());
+ String version = packageProperties.getProperty(PackageProperties.NAME_VERSION);
+ if (version == null || version.isEmpty()) {
+ version = DEFEAULT_VERSION;
+ }
- traverse(vaultPackage);
+ String description = packageProperties.getDescription();
+
+ featuresManager.init(group,
+ name,
+ version,
+ description);
+
+ logger.info("Converting content-package '{}'...", vaultPackage.getId());
+
+ traverse(vaultPackage);
- // attach all unmatched resources as new content-package
+ // attach all unmatched resources as new content-package
- File contentPackageArchive = mainPackageAssembler.createPackage();
+ File contentPackageArchive = mainPackageAssembler.createPackage();
- // deploy the new zip content-package to the local mvn bundles dir
+ // deploy the new zip content-package to the local mvn bundles dir
- artifactsDeployer.deploy(new FileArtifactWriter(contentPackageArchive),
- featuresManager.getTargetFeature().getId().getGroupId(),
- featuresManager.getTargetFeature().getId().getArtifactId(),
- featuresManager.getTargetFeature().getId().getVersion(),
- PACKAGE_CLASSIFIER,
- ZIP_TYPE);
+ artifactsDeployer.deploy(new FileArtifactWriter(contentPackageArchive),
+ featuresManager.getTargetFeature().getId().getGroupId(),
+ featuresManager.getTargetFeature().getId().getArtifactId(),
+ featuresManager.getTargetFeature().getId().getVersion(),
+ PACKAGE_CLASSIFIER,
+ ZIP_TYPE);
- featuresManager.addArtifact(null,
- featuresManager.getTargetFeature().getId().getGroupId(),
- featuresManager.getTargetFeature().getId().getArtifactId(),
- featuresManager.getTargetFeature().getId().getVersion(),
- PACKAGE_CLASSIFIER,
- ZIP_TYPE);
+ featuresManager.addArtifact(null,
+ featuresManager.getTargetFeature().getId().getGroupId(),
+ featuresManager.getTargetFeature().getId().getArtifactId(),
+ featuresManager.getTargetFeature().getId().getVersion(),
+ PACKAGE_CLASSIFIER,
+ ZIP_TYPE);
- // finally serialize the Feature Model(s) file(s)
+ // finally serialize the Feature Model(s) file(s)
- aclManager.addRepoinitExtension(mainPackageAssembler, featuresManager.getTargetFeature());
+ aclManager.addRepoinitExtension(mainPackageAssembler, featuresManager.getTargetFeature());
- logger.info("Conversion complete!");
+ logger.info("Conversion complete!");
- featuresManager.serialize();
+ featuresManager.serialize();
- aclManager.reset();
+ aclManager.reset();
+ } finally {
+ try {
+ vaultPackage.close();
+ } catch (Exception e) {
+ // close quietly
+ }
+ }
}
}
+ protected Collection<VaultPackage> firstPass(File...contentPackages) throws Exception {
+ Map<PackageId, VaultPackage> idFileMap = new LinkedHashMap<>();
+ Map<PackageId, VaultPackage> idPackageMapping = new HashMap<>();
+
+ for (File contentPackage : contentPackages) {
+ requireNonNull(contentPackage, "Null content-package can not be converted.");
+
+ if (!contentPackage.exists() || !contentPackage.isFile()) {
+ throw new IllegalArgumentException("File " + contentPackage + " does not exist or it is a directory");
+ }
+
+ logger.info("Reading content-package '{}'...", contentPackage);
+
+ VaultPackage pack = packageManager.open(contentPackage, isStrictValidation());
+ idPackageMapping.put(pack.getId(), pack);
+
+ logger.info("content-package '{}' successfully read!", contentPackage);
+ }
+
+ for (VaultPackage pack : new HashSet<VaultPackage>(idPackageMapping.values())) {
+ orderDependencies(idFileMap, idPackageMapping, pack, new HashSet<PackageId>());
+ }
+
+ return idFileMap.values();
+ }
+
+ private void orderDependencies(Map<PackageId, VaultPackage> idFileMap,
+ Map<PackageId, VaultPackage> idPackageMapping,
+ VaultPackage pack,
+ Set<PackageId> visited) throws CyclicDependencyException {
+ if (!visited.add(pack.getId())) {
+ throw new CyclicDependencyException("Cyclic dependency detected, " + pack.getId() + " was previously visited already");
+ }
+
+ for (Dependency dep : pack.getDependencies()) {
+ for (PackageId id : idPackageMapping.keySet()) {
+ if (dep.matches(id)) {
+ orderDependencies(idFileMap, idPackageMapping, idPackageMapping.get(id), visited);
+ break;
+ }
+ }
+ }
+
+ idFileMap.put(pack.getId(), pack);
+ idPackageMapping.remove(pack.getId());
+ }
+
public void processSubPackage(String path, File contentPackage) throws Exception {
requireNonNull(path, "Impossible to process a null vault package");
requireNonNull(contentPackage, "Impossible to process a null vault package");
diff --git a/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
index 71f7755..ac4b92b 100644
--- a/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
+++ b/src/main/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncher.java
@@ -17,20 +17,10 @@
package org.apache.sling.feature.cpconverter.cli;
import java.io.File;
-import java.io.IOException;
-import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.TimeZone;
-import org.apache.jackrabbit.vault.packaging.CyclicDependencyException;
-import org.apache.jackrabbit.vault.packaging.Dependency;
-import org.apache.jackrabbit.vault.packaging.PackageId;
-import org.apache.jackrabbit.vault.packaging.impl.ZipVaultPackage;
import org.apache.sling.feature.cpconverter.ContentPackage2FeatureModelConverter;
import org.apache.sling.feature.cpconverter.acl.DefaultAclManager;
import org.apache.sling.feature.cpconverter.artifacts.DefaultArtifactsDeployer;
@@ -89,7 +79,7 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna
private Map<String, String> properties = new HashMap<>();
@Parameters(arity = "1..*", paramLabel = "content-packages", description = "The content-package input file(s).")
- private List<File> contentPackages;
+ private File[] contentPackages;
@Override
public void run() {
@@ -139,15 +129,7 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna
converter.setResourceFilter(filter);
}
- logger.info("Ordering input content-package(s) {}...", contentPackages);
-
- Collection<File> orderedContentPackages = order(contentPackages);
-
- logger.info("New content-package(s) order: {}", orderedContentPackages);
-
- for (File contentPackage : orderedContentPackages) {
- converter.convert(contentPackage);
- }
+ converter.convert(contentPackages);
logger.info( "+-----------------------------------------------------+" );
logger.info("{} SUCCESS", appName);
@@ -168,55 +150,6 @@ public final class ContentPackage2FeatureModelConverterLauncher implements Runna
}
}
- protected Collection<File> order(List<File> contentPackages) throws Exception {
- Map<PackageId, File> idFileMap = new LinkedHashMap<>();
- Map<ZipVaultPackage, File> packageFileMapping = new HashMap<>();
- Map<PackageId, ZipVaultPackage> idPackageMapping = new HashMap<>();
-
- for (File file : contentPackages) {
- if (!file.exists() || file.isDirectory()) {
- throw new Exception("File " + file + " does not exist or it is a directory");
- }
-
- try {
- ZipVaultPackage pack = new ZipVaultPackage(file, false, true);
- packageFileMapping.put(pack, file);
- idPackageMapping.put(pack.getId(), pack);
- } catch (IOException e) {
- String errorMessage = String.format("Package couldn't be parsed as ZipVaultPackage %s", file.getAbsolutePath());
- throw new Exception(errorMessage);
- }
- }
-
- for (ZipVaultPackage pack : packageFileMapping.keySet()) {
- orderDependencies(idFileMap, packageFileMapping, idPackageMapping, pack, new HashSet<PackageId>());
- }
-
- return idFileMap.values();
- }
-
- private void orderDependencies(Map<PackageId, File> idFileMap,
- Map<ZipVaultPackage, File> packageFileMapping,
- Map<PackageId, ZipVaultPackage> idPackageMapping,
- ZipVaultPackage pack,
- Set<PackageId> visited) throws CyclicDependencyException {
- if (!visited.add(pack.getId())) {
- throw new CyclicDependencyException("Cyclic dependency detected, " + pack.getId() + " was previously visited already");
- }
-
- for (Dependency dep : pack.getDependencies()) {
- for (PackageId id : idPackageMapping.keySet()) {
- if (dep.matches(id)) {
- orderDependencies(idFileMap, packageFileMapping, idPackageMapping, idPackageMapping.get(id), visited);
- break;
- }
- }
- }
-
- idFileMap.put(pack.getId(), packageFileMapping.get(pack));
- idPackageMapping.remove(pack.getId());
- }
-
private static void printVersion(final Logger logger) {
logger.info("{} v{} (built on {})",
System.getProperty("project.artifactId"),
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
index 556ab04..5ccb4ab 100644
--- a/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
+++ b/src/test/java/org/apache/sling/feature/cpconverter/ContentPackage2FeatureModelConverterTest.java
@@ -27,14 +27,18 @@ import java.io.FileReader;
import java.io.Reader;
import java.net.URL;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.zip.ZipFile;
import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.vault.packaging.CyclicDependencyException;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
import org.apache.sling.feature.ArtifactId;
import org.apache.sling.feature.Feature;
import org.apache.sling.feature.cpconverter.acl.DefaultAclManager;
@@ -49,6 +53,20 @@ import org.junit.Test;
public class ContentPackage2FeatureModelConverterTest {
+ /**
+ * Test package A-1.0. Depends on B and C-1.X
+ * Test package B-1.0. Depends on C
+ */
+ private static String[] TEST_PACKAGES_INPUT = { "test_c-1.0.zip", "test_a-1.0.zip", "test_b-1.0.zip" };
+
+ private static String[] TEST_PACKAGES_OUTPUT = { "my_packages:test_c:1.0", "my_packages:test_b:1.0", "my_packages:test_a:1.0" };
+
+ private static String[] TEST_PACKAGES_CYCLIC_DEPENDENCY = { "test_d-1.0.zip",
+ "test_c-1.0.zip",
+ "test_a-1.0.zip",
+ "test_b-1.0.zip",
+ "test_e-1.0.zip" };
+
private ContentPackage2FeatureModelConverter converter;
@Before
@@ -65,7 +83,12 @@ public class ContentPackage2FeatureModelConverterTest {
@Test(expected = NullPointerException.class)
public void convertRequiresNonNullPackage() throws Exception {
- converter.convert(null);
+ converter.convert((File)null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void convertRequiresNonNullPackages() throws Exception {
+ converter.convert((File[])null);
}
@Test(expected = IllegalArgumentException.class)
@@ -294,4 +317,36 @@ public class ContentPackage2FeatureModelConverterTest {
}
zipFile.close();
}
+
+ @Test
+ public void testPackageOrdering() throws Exception {
+ File[] contentPackages = load(TEST_PACKAGES_INPUT);
+
+ Collection<VaultPackage> ordered = converter.firstPass(contentPackages);
+
+ Iterator<VaultPackage> fileIt = ordered.iterator();
+ for (String expected : TEST_PACKAGES_OUTPUT) {
+ VaultPackage next = fileIt.next();
+ assertEquals(expected, next.getId().toString());
+ }
+ }
+
+ @Test(expected = CyclicDependencyException.class)
+ public void testDependencyCycle() throws Exception {
+ File[] contentPackages = load(TEST_PACKAGES_CYCLIC_DEPENDENCY);
+ converter.firstPass(contentPackages);
+ }
+
+ private File[] load(String...resources) {
+ File[] loadedResources = new File[resources.length];
+
+ for (int i = 0; i < resources.length; i++) {
+ String resourceName = resources[i];
+ URL resourceUrl = getClass().getResource(resourceName);
+ loadedResources[i] = FileUtils.toFile(resourceUrl);
+ }
+
+ return loadedResources;
+ }
+
}
diff --git a/src/test/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncherTest.java b/src/test/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncherTest.java
deleted file mode 100644
index d65bcbf..0000000
--- a/src/test/java/org/apache/sling/feature/cpconverter/cli/ContentPackage2FeatureModelConverterLauncherTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You 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 org.apache.sling.feature.cpconverter.cli;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.File;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.jackrabbit.vault.packaging.CyclicDependencyException;
-import org.junit.Test;
-
-public class ContentPackage2FeatureModelConverterLauncherTest {
-
- /**
- * Test package A-1.0. Depends on B and C-1.X
- * Test package B-1.0. Depends on C
- */
- private static String[] TEST_PACKAGES_INPUT = { "test_c-1.0.zip", "test_a-1.0.zip", "test_b-1.0.zip" };
-
- private static String[] TEST_PACKAGES_OUTPUT = { "test_c-1.0.zip", "test_b-1.0.zip", "test_a-1.0.zip" };
-
- private static String[] TEST_PACKAGES_CYCLIC_DEPENDENCY = { "test_d-1.0.zip",
- "test_c-1.0.zip",
- "test_a-1.0.zip",
- "test_b-1.0.zip",
- "test_e-1.0.zip" };
-
- @Test
- public void testPackageOrdering() throws Exception {
- ContentPackage2FeatureModelConverterLauncher launcher = new ContentPackage2FeatureModelConverterLauncher();
- List<File> contentPackages = new ArrayList<File>();
-
- for (String pkgName : TEST_PACKAGES_INPUT) {
- URL packageUrl = getClass().getResource(pkgName);
- contentPackages.add(FileUtils.toFile(packageUrl));
- }
- Collection<File> ordered = launcher.order(contentPackages);
- Iterator<File> fileIt = ordered.iterator();
- for (String expected : TEST_PACKAGES_OUTPUT) {
- File next = fileIt.next();
- assertEquals(expected, next.getName());
- }
- }
-
- @Test(expected = CyclicDependencyException.class)
- public void testDependencyCycle() throws Exception {
- ContentPackage2FeatureModelConverterLauncher launcher = new ContentPackage2FeatureModelConverterLauncher();
- List<File> contentPackages = new ArrayList<File>();
-
- for (String pkgName : TEST_PACKAGES_CYCLIC_DEPENDENCY) {
- URL packageUrl = getClass().getResource(pkgName);
- contentPackages.add(FileUtils.toFile(packageUrl));
- }
- launcher.order(contentPackages);
- }
-
-}
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_a-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/test_a-1.0.zip
similarity index 100%
rename from src/test/resources/org/apache/sling/feature/cpconverter/cli/test_a-1.0.zip
rename to src/test/resources/org/apache/sling/feature/cpconverter/test_a-1.0.zip
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_b-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/test_b-1.0.zip
similarity index 100%
rename from src/test/resources/org/apache/sling/feature/cpconverter/cli/test_b-1.0.zip
rename to src/test/resources/org/apache/sling/feature/cpconverter/test_b-1.0.zip
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_c-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/test_c-1.0.zip
similarity index 100%
rename from src/test/resources/org/apache/sling/feature/cpconverter/cli/test_c-1.0.zip
rename to src/test/resources/org/apache/sling/feature/cpconverter/test_c-1.0.zip
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_d-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/test_d-1.0.zip
similarity index 100%
rename from src/test/resources/org/apache/sling/feature/cpconverter/cli/test_d-1.0.zip
rename to src/test/resources/org/apache/sling/feature/cpconverter/test_d-1.0.zip
diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/cli/test_e-1.0.zip b/src/test/resources/org/apache/sling/feature/cpconverter/test_e-1.0.zip
similarity index 100%
rename from src/test/resources/org/apache/sling/feature/cpconverter/cli/test_e-1.0.zip
rename to src/test/resources/org/apache/sling/feature/cpconverter/test_e-1.0.zip