Thomas Vachuska
Committed by Gerrit Code Review

ONOS-542 Added ability for app bundle to carry it's own artifacts, including fea…

…ture repo. Fixed onos-package script. Added JSON output to CLI.

Change-Id: If4f2c774d3fc2d68c0a8e91b3084b99d7c75d927
......@@ -18,6 +18,7 @@ package org.onosproject.cli;
import java.util.Comparator;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.core.Application;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Element;
......@@ -43,6 +44,13 @@ public final class Comparators {
}
};
public static final Comparator<Application> APP_COMPARATOR = new Comparator<Application>() {
@Override
public int compare(Application app1, Application app2) {
return app1.id().id() - app2.id().id();
}
};
public static final Comparator<ElementId> ELEMENT_ID_COMPARATOR = new Comparator<ElementId>() {
@Override
public int compare(ElementId id1, ElementId id2) {
......
......@@ -15,11 +15,19 @@
*/
package org.onosproject.cli.app;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.app.ApplicationService;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.cli.Comparators;
import org.onosproject.core.Application;
import java.util.Collections;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static org.onosproject.app.ApplicationState.ACTIVE;
/**
......@@ -36,11 +44,44 @@ public class ApplicationsListCommand extends AbstractShellCommand {
@Override
protected void execute() {
ApplicationService service = get(ApplicationService.class);
for (Application app : service.getApplications()) {
print(FMT, service.getState(app.id()) == ACTIVE ? "*" : " ",
app.id().id(), app.id().name(), app.version(), app.origin(),
app.description(), app.features(), app.featuresRepo(), app.permissions());
List<Application> apps = newArrayList(service.getApplications());
Collections.sort(apps, Comparators.APP_COMPARATOR);
if (outputJson()) {
print("%s", json(service, apps));
} else {
for (Application app : apps) {
print(FMT, service.getState(app.id()) == ACTIVE ? "*" : " ",
app.id().id(), app.id().name(), app.version(), app.origin(),
app.description(), app.features(),
app.featuresRepo().isPresent() ? app.featuresRepo().get().toString() : "",
app.permissions());
}
}
}
private JsonNode json(ApplicationService service, List<Application> apps) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
for (Application app : apps) {
result.add(json(service, mapper, app));
}
return result;
}
protected JsonNode json(ApplicationService service, ObjectMapper mapper,
Application app) {
return mapper.createObjectNode()
.put("name", app.id().name())
.put("id", app.id().id())
.put("version", app.version().toString())
.put("description", app.description())
.put("origin", app.origin())
.put("permissions", app.permissions().toString())
.put("featuresRepo", app.featuresRepo().isPresent() ?
app.featuresRepo().get().toString() : "")
.put("features", app.features().toString())
.put("state", service.getState(app.id()).toString());
}
}
......
......@@ -19,6 +19,7 @@ import org.onosproject.core.Permission;
import org.onosproject.core.Version;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.Set;
......@@ -71,10 +72,10 @@ public interface ApplicationDescription {
Optional<URI> featuresRepo();
/**
* Returns the set of features comprising the application. At least one
* Returns the list of features comprising the application. At least one
* feature must be given.
*
* @return application features
*/
Set<String> features();
List<String> features();
}
......
......@@ -19,6 +19,7 @@ import org.onosproject.core.Permission;
import org.onosproject.core.Version;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.Set;
......@@ -37,7 +38,7 @@ public class DefaultApplicationDescription implements ApplicationDescription {
private final String origin;
private final Set<Permission> permissions;
private final Optional<URI> featuresRepo;
private final Set<String> features;
private final List<String> features;
/**
* Creates a new application descriptor using the supplied data.
......@@ -53,7 +54,7 @@ public class DefaultApplicationDescription implements ApplicationDescription {
public DefaultApplicationDescription(String name, Version version,
String description, String origin,
Set<Permission> permissions,
URI featuresRepo, Set<String> features) {
URI featuresRepo, List<String> features) {
this.name = checkNotNull(name, "Name cannot be null");
this.version = checkNotNull(version, "Version cannot be null");
this.description = checkNotNull(description, "Description cannot be null");
......@@ -95,7 +96,7 @@ public class DefaultApplicationDescription implements ApplicationDescription {
}
@Override
public Set<String> features() {
public List<String> features() {
return features;
}
......
......@@ -16,6 +16,7 @@
package org.onosproject.core;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.Set;
......@@ -68,10 +69,10 @@ public interface Application {
Optional<URI> featuresRepo();
/**
* Returns the set of features comprising the application. At least one
* Returns the list of features comprising the application. At least one
* feature must be given.
*
* @return application features
*/
Set<String> features();
List<String> features();
}
......
......@@ -16,6 +16,7 @@
package org.onosproject.core;
import java.net.URI;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
......@@ -35,7 +36,7 @@ public class DefaultApplication implements Application {
private final String origin;
private final Set<Permission> permissions;
private final Optional<URI> featuresRepo;
private final Set<String> features;
private final List<String> features;
/**
* Creates a new application descriptor using the supplied data.
......@@ -51,7 +52,7 @@ public class DefaultApplication implements Application {
public DefaultApplication(ApplicationId appId, Version version,
String description, String origin,
Set<Permission> permissions,
Optional<URI> featuresRepo, Set<String> features) {
Optional<URI> featuresRepo, List<String> features) {
this.appId = checkNotNull(appId, "ID cannot be null");
this.version = checkNotNull(version, "Version cannot be null");
this.description = checkNotNull(description, "Description cannot be null");
......@@ -93,7 +94,7 @@ public class DefaultApplication implements Application {
}
@Override
public Set<String> features() {
public List<String> features() {
return features;
}
......
......@@ -15,12 +15,14 @@
*/
package org.onosproject.app;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
import org.onosproject.core.Permission;
import org.onosproject.core.Version;
import java.net.URI;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertEquals;
......@@ -37,7 +39,7 @@ public class DefaultApplicationDescriptionTest {
public static final String ORIGIN = "Circus";
public static final Set<Permission> PERMS = ImmutableSet.of();
public static final URI FURL = URI.create("mvn:org.foo-features/1.2a/xml/features");
public static final Set<String> FEATURES = ImmutableSet.of("foo");
public static final List<String> FEATURES = ImmutableList.of("foo", "bar");
@Test
public void basics() {
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.common.app;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
......@@ -40,6 +41,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.NoSuchFileException;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
......@@ -191,8 +193,7 @@ public class ApplicationArchive
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().equals(APP_XML)) {
byte[] data = new byte[(int) entry.getSize()];
ByteStreams.readFully(zis, data);
byte[] data = ByteStreams.toByteArray(zis);
XMLConfiguration cfg = new XMLConfiguration();
try {
cfg.load(new ByteArrayInputStream(data));
......@@ -209,6 +210,7 @@ public class ApplicationArchive
private ApplicationDescription loadAppDescription(XMLConfiguration cfg) {
cfg.setAttributeSplittingDisabled(true);
cfg.setDelimiterParsingDisabled(true);
String name = cfg.getString(NAME);
Version version = Version.version(cfg.getString(VERSION));
String desc = cfg.getString(DESCRIPTION);
......@@ -216,7 +218,7 @@ public class ApplicationArchive
Set<Permission> perms = ImmutableSet.of();
String featRepo = cfg.getString(FEATURES_REPO);
URI featuresRepo = featRepo != null ? URI.create(featRepo) : null;
Set<String> features = ImmutableSet.copyOf(cfg.getString(FEATURES).split(","));
List<String> features = ImmutableList.copyOf(cfg.getStringArray(FEATURES));
return new DefaultApplicationDescription(name, version, desc, origin,
perms, featuresRepo, features);
......@@ -229,13 +231,14 @@ public class ApplicationArchive
ZipEntry entry;
File appDir = new File(appsDir, desc.name());
while ((entry = zis.getNextEntry()) != null) {
byte[] data = new byte[(int) entry.getSize()];
ByteStreams.readFully(zis, data);
zis.closeEntry();
if (!entry.isDirectory()) {
byte[] data = ByteStreams.toByteArray(zis);
zis.closeEntry();
File file = new File(appDir, entry.getName());
createParentDirs(file);
write(data, file);
File file = new File(appDir, entry.getName());
createParentDirs(file);
write(data, file);
}
}
zis.close();
}
......
......@@ -46,9 +46,16 @@ sed "s/\$KARAF_VERSION/$KARAF_VERSION/g" \
sed "s/\$KARAF_VERSION/$KARAF_VERSION/g" \
$ONOS_ROOT/tools/package/bin/onos > bin/onos
# Stage the ONOS bundles
# Stage the ONOS bundles, but only those that match the version
mkdir -p $KARAF_DIST/system/org/onosproject
cp -r $M2_REPO/org/onosproject $KARAF_DIST/system/org/
# cp -r $M2_REPO/org/onosproject/ $KARAF_DIST/system/org/
find $M2_REPO/org/onosproject/ -type d -name $ONOS_POM_VERSION | while read line; do
path=${line#*/onosproject/}
artifact=${path%/$ONOS_POM_VERSION}
mkdir -p $KARAF_DIST/system/org/onosproject/$artifact
cp -r $M2_REPO/org/onosproject/$artifact/$ONOS_POM_VERSION \
$KARAF_DIST/system/org/onosproject/$artifact/$ONOS_POM_VERSION
done
export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-foo}"
......
......@@ -40,7 +40,7 @@ public final class ApplicationCodec extends JsonCodec<Application> {
.put("origin", app.origin())
.put("permissions", app.permissions().toString())
.put("featuresRepo", app.featuresRepo().isPresent() ?
app.featuresRepo().toString() : "")
app.featuresRepo().get().toString() : "")
.put("features", app.features().toString())
.put("state", service.getState(app.id()).toString());
return result;
......
......@@ -15,10 +15,11 @@
*/
package org.onosproject.rest;
import java.io.InputStream;
import java.net.URI;
import java.util.Optional;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.sun.jersey.api.client.WebResource;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
......@@ -40,22 +41,14 @@ import org.onosproject.core.DefaultApplication;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.core.Version;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.google.common.collect.ImmutableSet;
import com.sun.jersey.api.client.WebResource;
import java.io.InputStream;
import java.net.URI;
import java.util.Optional;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.*;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.*;
/**
* Unit tests for applications REST APIs.
......@@ -88,20 +81,20 @@ public class ApplicationsResourceTest extends ResourceTest {
private Application app1 =
new DefaultApplication(id1, VER,
"app1", "origin1", ImmutableSet.of(), Optional.of(FURL),
ImmutableSet.of("My Feature"));
"app1", "origin1", ImmutableSet.of(), Optional.of(FURL),
ImmutableList.of("My Feature"));
private Application app2 =
new DefaultApplication(id2, VER,
"app2", "origin2", ImmutableSet.of(), Optional.of(FURL),
ImmutableSet.of("My Feature"));
"app2", "origin2", ImmutableSet.of(), Optional.of(FURL),
ImmutableList.of("My Feature"));
private Application app3 =
new DefaultApplication(id3, VER,
"app3", "origin3", ImmutableSet.of(), Optional.of(FURL),
ImmutableSet.of("My Feature"));
"app3", "origin3", ImmutableSet.of(), Optional.of(FURL),
ImmutableList.of("My Feature"));
private Application app4 =
new DefaultApplication(id4, VER,
"app4", "origin4", ImmutableSet.of(), Optional.of(FURL),
ImmutableSet.of("My Feature"));
"app4", "origin4", ImmutableSet.of(), Optional.of(FURL),
ImmutableList.of("My Feature"));
/**
* Hamcrest matcher to check that an device representation in JSON matches
......