ONOS-542 Defining application subsystem interfaces & public constructs.

Change-Id: Iba0d2cb69dace5beee8a68def9918059ce755b5c
diff --git a/core/api/src/main/java/org/onosproject/app/ApplicationAdminService.java b/core/api/src/main/java/org/onosproject/app/ApplicationAdminService.java
new file mode 100644
index 0000000..18babd5
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationAdminService.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.Permission;
+
+import java.io.InputStream;
+import java.util.Set;
+
+/**
+ * Service for managing network control applications.
+ */
+public interface ApplicationAdminService extends ApplicationService {
+
+    /**
+     * Installs the application contained in the specified application archive
+     * input stream.
+     *
+     * @param appDescStream application descriptor input stream
+     * @return installed application descriptor
+     * @throws org.onosproject.app.ApplicationException if unable to read the app archive stream
+     */
+    Application install(InputStream appDescStream);
+
+    /**
+     * Uninstalls the specified application.
+     *
+     * @param appId application identifier
+     */
+    void uninstall(ApplicationId appId);
+
+    /**
+     * Activates the specified application.
+     *
+     * @param appId application identifier
+     */
+    void activate(ApplicationId appId);
+
+    /**
+     * Deactivates the specified application.
+     *
+     * @param appId application identifier
+     */
+    void deactivate(ApplicationId appId);
+
+    /**
+     * Updates the permissions granted to the applications.
+     *
+     * @param appId       application identifier
+     * @param permissions set of granted permissions
+     */
+    void setPermissions(ApplicationId appId, Set<Permission> permissions);
+}
diff --git a/core/api/src/main/java/org/onosproject/app/ApplicationDescription.java b/core/api/src/main/java/org/onosproject/app/ApplicationDescription.java
new file mode 100644
index 0000000..ec7958d
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationDescription.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onosproject.core.Permission;
+import org.onosproject.core.Version;
+
+import java.net.URI;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Description of a network control/management application.
+ */
+public interface ApplicationDescription {
+
+    /**
+     * Returns the application name id.
+     *
+     * @return application identifier
+     */
+    String name();
+
+    /**
+     * Returns the application version.
+     *
+     * @return application version
+     */
+    Version version();
+
+    /**
+     * Returns description of the application.
+     *
+     * @return application description text
+     */
+    String description();
+
+    /**
+     * Returns the name of the application origin, group or company.
+     *
+     * @return application origin
+     */
+    String origin();
+
+    /**
+     * Returns the permissions requested by the application.
+     *
+     * @return requested permissions
+     */
+    Set<Permission> permissions();
+
+    /**
+     * Returns the feature repository URI. Null value signifies that the
+     * application did not provide its own features repository.
+     *
+     * @return optional feature repo URL
+     */
+    Optional<URI> featuresRepo();
+
+    /**
+     * Returns the set of features comprising the application. At least one
+     * feature must be given.
+     *
+     * @return application features
+     */
+    Set<String> features();
+}
diff --git a/core/api/src/main/java/org/onosproject/app/ApplicationEvent.java b/core/api/src/main/java/org/onosproject/app/ApplicationEvent.java
new file mode 100644
index 0000000..5bf1323
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationEvent.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onosproject.core.Application;
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Describes application lifecycle event.
+ */
+public class ApplicationEvent extends AbstractEvent<ApplicationEvent.Type, Application> {
+
+    public enum Type {
+        /**
+         * Signifies that an application has been installed.
+         */
+        APP_INSTALLED,
+
+        /**
+         * Signifies that an application has been activated.
+         */
+        APP_ACTIVATED,
+
+        /**
+         * Signifies that an application has been deactivated.
+         */
+        APP_DEACTIVATED,
+
+        /**
+         * Signifies that an application has been uninstalled.
+         */
+        APP_UNINSTALLED,
+
+        /**
+         * Signifies that application granted permissions have changed.
+         */
+        APP_PERMISSIONS_CHANGED
+    }
+
+    /**
+     * Creates an event of a given type and for the specified app and the
+     * current time.
+     *
+     * @param type app event type
+     * @param app  event app subject
+     */
+    public ApplicationEvent(Type type, Application app) {
+        super(type, app);
+    }
+
+    /**
+     * Creates an event of a given type and for the specified app and time.
+     *
+     * @param type app event type
+     * @param app  event app subject
+     * @param time occurrence time
+     */
+    public ApplicationEvent(Type type, Application app, long time) {
+        super(type, app, time);
+    }
+
+}
diff --git a/core/api/src/main/java/org/onosproject/app/ApplicationException.java b/core/api/src/main/java/org/onosproject/app/ApplicationException.java
new file mode 100644
index 0000000..2888c70
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+/**
+ * Represents class of errors related to application management.
+ */
+public class ApplicationException extends RuntimeException {
+
+    private static final long serialVersionUID = -2287403908433720122L;
+
+    /**
+     * Constructs an exception with no message and no underlying cause.
+     */
+    public ApplicationException() {
+    }
+
+    /**
+     * Constructs an exception with the specified message.
+     *
+     * @param message the message describing the specific nature of the error
+     */
+    public ApplicationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs an exception with the specified message and the underlying cause.
+     *
+     * @param message the message describing the specific nature of the error
+     * @param cause the underlying cause of this error
+     */
+    public ApplicationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java b/core/api/src/main/java/org/onosproject/app/ApplicationListener.java
similarity index 68%
copy from core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
copy to core/api/src/main/java/org/onosproject/app/ApplicationListener.java
index e39533c..7a68057 100644
--- a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.json.impl;
+package org.onosproject.app;
+
+import org.onosproject.event.EventListener;
 
 /**
- * Created by tom on 10/16/14.
+ * Entity capable of receiving application related events.
  */
-public class DeleteMe {
+public interface ApplicationListener extends EventListener<ApplicationEvent> {
 }
diff --git a/core/api/src/main/java/org/onosproject/app/ApplicationService.java b/core/api/src/main/java/org/onosproject/app/ApplicationService.java
new file mode 100644
index 0000000..1aaf092
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.Permission;
+
+import java.util.Set;
+
+/**
+ * Service for inspecting inventory of network control applications.
+ */
+public interface ApplicationService {
+
+    /**
+     * Returns the set of all installed applications.
+     *
+     * @return set of installed apps
+     */
+    Set<Application> getApplications();
+
+    /**
+     * Returns the registered id of the application with the given name.
+     *
+     * @param name application name
+     * @return registered application id
+     */
+    ApplicationId getId(String name);
+
+    /**
+     * Returns the application with the supplied application identifier.
+     *
+     * @param appId application identifier
+     * @return application descriptor
+     */
+    Application getApplication(ApplicationId appId);
+
+    /**
+     * Return the application state.
+     *
+     * @param appId application identifier
+     * @return application state
+     */
+    ApplicationState getState(ApplicationId appId);
+
+    /**
+     * Returns the permissions currently granted to the applications.
+     *
+     * @param appId application identifier
+     * @return set of granted permissions
+     */
+    Set<Permission> getPermissions(ApplicationId appId);
+
+    /**
+     * Adds the given listener for application lifecycle events.
+     *
+     * @param listener listener to be added
+     */
+    void addListener(ApplicationListener listener);
+
+    /**
+     * Removes the specified listener for application lifecycle events.
+     *
+     * @param listener listener to be removed
+     */
+    void removeListener(ApplicationListener listener);
+
+}
diff --git a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java b/core/api/src/main/java/org/onosproject/app/ApplicationState.java
similarity index 63%
copy from core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
copy to core/api/src/main/java/org/onosproject/app/ApplicationState.java
index e39533c..c480a0c 100644
--- a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationState.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,10 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.json.impl;
+package org.onosproject.app;
 
 /**
- * Created by tom on 10/16/14.
+ * Representation of an application state.
  */
-public class DeleteMe {
+public enum ApplicationState {
+
+    /**
+     * Indicates that application has been installed, but is not running.
+     */
+    INSTALLED,
+
+    /**
+     * Indicates that application is active.
+     */
+    ACTIVE
+
 }
diff --git a/core/api/src/main/java/org/onosproject/app/ApplicationStore.java b/core/api/src/main/java/org/onosproject/app/ApplicationStore.java
new file mode 100644
index 0000000..d20adb5
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationStore.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.Permission;
+import org.onosproject.store.Store;
+
+import java.io.InputStream;
+import java.util.Set;
+
+/**
+ * Service for managing network control applications.
+ */
+public interface ApplicationStore extends Store<ApplicationEvent, ApplicationStoreDelegate> {
+
+    /**
+     * Returns the set of all installed applications.
+     *
+     * @return set of installed apps
+     */
+    Set<Application> getApplications();
+
+    /**
+     * Returns the registered id of the application with the given name.
+     *
+     * @param name application name
+     * @return registered application id
+     */
+    ApplicationId getId(String name);
+
+    /**
+     * Returns the application with the supplied application identifier.
+     *
+     * @param appId application identifier
+     * @return application descriptor
+     */
+    Application getApplication(ApplicationId appId);
+
+    /**
+     * Returns the current application state.
+     *
+     * @param appId application identifier
+     * @return application state
+     */
+    ApplicationState getState(ApplicationId appId);
+
+    /**
+     * Creates the application from the specified application descriptor
+     * input stream.
+     *
+     * @param appDescStream application archive input stream
+     * @return application descriptor
+     */
+    Application create(InputStream appDescStream);
+
+    /**
+     * Removes the specified application.
+     *
+     * @param appId application identifier
+     */
+    void remove(ApplicationId appId);
+
+    /**
+     * Mark the application as actived.
+     *
+     * @param appId application identifier
+     */
+    void activate(ApplicationId appId);
+
+    /**
+     * Mark the application as deactivated.
+     *
+     * @param appId application identifier
+     */
+    void deactivate(ApplicationId appId);
+
+    /**
+     * Returns the permissions granted to the applications.
+     *
+     * @param appId application identifier
+     * @return set of granted permissions
+     */
+    Set<Permission> getPermissions(ApplicationId appId);
+
+    /**
+     * Updates the permissions granted to the applications.
+     *
+     * @param appId       application identifier
+     * @param permissions set of granted permissions
+     */
+    void setPermissions(ApplicationId appId, Set<Permission> permissions);
+
+}
diff --git a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java b/core/api/src/main/java/org/onosproject/app/ApplicationStoreDelegate.java
similarity index 69%
copy from core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
copy to core/api/src/main/java/org/onosproject/app/ApplicationStoreDelegate.java
index e39533c..f339e68 100644
--- a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
+++ b/core/api/src/main/java/org/onosproject/app/ApplicationStoreDelegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.json.impl;
+package org.onosproject.app;
+
+import org.onosproject.store.StoreDelegate;
 
 /**
- * Created by tom on 10/16/14.
+ * Application store delegate abstraction.
  */
-public class DeleteMe {
+public interface ApplicationStoreDelegate extends StoreDelegate<ApplicationEvent> {
 }
diff --git a/core/api/src/main/java/org/onosproject/app/DefaultApplicationDescription.java b/core/api/src/main/java/org/onosproject/app/DefaultApplicationDescription.java
new file mode 100644
index 0000000..9c53542
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/app/DefaultApplicationDescription.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onosproject.core.Permission;
+import org.onosproject.core.Version;
+
+import java.net.URI;
+import java.util.Optional;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default implementation of network control/management application descriptor.
+ */
+public class DefaultApplicationDescription implements ApplicationDescription {
+
+    private final String name;
+    private final Version version;
+    private final String description;
+    private final String origin;
+    private final Set<Permission> permissions;
+    private final Optional<URI> featuresRepo;
+    private final Set<String> features;
+
+    /**
+     * Creates a new application descriptor using the supplied data.
+     *
+     * @param name         application name
+     * @param version      application version
+     * @param description  application description
+     * @param origin       origin company
+     * @param permissions  requested permissions
+     * @param featuresRepo optional features repo URI
+     * @param features     application features
+     */
+    public DefaultApplicationDescription(String name, Version version,
+                                         String description, String origin,
+                                         Set<Permission> permissions,
+                                         URI featuresRepo, Set<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");
+        this.origin = checkNotNull(origin, "Origin cannot be null");
+        this.permissions = checkNotNull(permissions, "Permissions cannot be null");
+        this.featuresRepo = Optional.ofNullable(featuresRepo);
+        this.features = checkNotNull(features, "Features cannot be null");
+        checkArgument(!features.isEmpty(), "There must be at least one feature");
+    }
+
+    @Override
+    public String name() {
+        return name;
+    }
+
+    @Override
+    public Version version() {
+        return version;
+    }
+
+    @Override
+    public String description() {
+        return description;
+    }
+
+    @Override
+    public String origin() {
+        return origin;
+    }
+
+    @Override
+    public Set<Permission> permissions() {
+        return permissions;
+    }
+
+    @Override
+    public Optional<URI> featuresRepo() {
+        return featuresRepo;
+    }
+
+    @Override
+    public Set<String> features() {
+        return features;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("name", name)
+                .add("version", version)
+                .add("description", description)
+                .add("origin", origin)
+                .add("permissions", permissions)
+                .add("featuresRepo", featuresRepo)
+                .add("features", features)
+                .toString();
+    }
+}
diff --git a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java b/core/api/src/main/java/org/onosproject/app/package-info.java
similarity index 79%
copy from core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
copy to core/api/src/main/java/org/onosproject/app/package-info.java
index e39533c..f8e5465 100644
--- a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
+++ b/core/api/src/main/java/org/onosproject/app/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,10 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.json.impl;
 
 /**
- * Created by tom on 10/16/14.
+ * Set of abstractions for managing network control applications.
  */
-public class DeleteMe {
-}
+package org.onosproject.app;
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onosproject/core/Application.java b/core/api/src/main/java/org/onosproject/core/Application.java
new file mode 100644
index 0000000..d876a67
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/core/Application.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.core;
+
+import java.net.URI;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Abstraction of a network control/management application.
+ */
+public interface Application {
+
+    /**
+     * Returns the application name id.
+     *
+     * @return application identifier
+     */
+    ApplicationId id();
+
+    /**
+     * Returns the application version.
+     *
+     * @return application version
+     */
+    Version version();
+
+    /**
+     * Returns description of the application.
+     *
+     * @return application description text
+     */
+    String description();
+
+    /**
+     * Returns the name of the application origin, group or company.
+     *
+     * @return application origin
+     */
+    String origin();
+
+    /**
+     * Returns the permissions requested by the application.
+     *
+     * @return requested permissions
+     */
+    Set<Permission> permissions();
+
+    /**
+     * Returns the feature repository URI. Null value signifies that the
+     * application did not provide its own features repository.
+     *
+     * @return optional feature repo URL
+     */
+    Optional<URI> featuresRepo();
+
+    /**
+     * Returns the set of features comprising the application. At least one
+     * feature must be given.
+     *
+     * @return application features
+     */
+    Set<String> features();
+}
diff --git a/core/api/src/main/java/org/onosproject/core/ApplicationIdStore.java b/core/api/src/main/java/org/onosproject/core/ApplicationIdStore.java
index cef3f15..2236fc5 100644
--- a/core/api/src/main/java/org/onosproject/core/ApplicationIdStore.java
+++ b/core/api/src/main/java/org/onosproject/core/ApplicationIdStore.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 package org.onosproject.core;
+// FIXME: Move to org.onosproject.app package
 
 import java.util.Set;
 
@@ -31,12 +32,21 @@
 
     /**
      * Returns an existing application id from a given id.
+     *
      * @param id the short value of the id
-     * @return an application id
+     * @return an application id; null if no such app registered
      */
     ApplicationId getAppId(Short id);
 
     /**
+     * Returns registered application id from the given name.
+     *
+     * @param name application name
+     * @return an application id; null if no such app registered
+     */
+    ApplicationId getAppId(String name);
+
+    /**
      * Registers a new application by its name, which is expected
      * to follow the reverse DNS convention, e.g.
      * {@code org.flying.circus.app}
diff --git a/core/api/src/main/java/org/onosproject/core/DefaultApplication.java b/core/api/src/main/java/org/onosproject/core/DefaultApplication.java
new file mode 100644
index 0000000..4da85a5
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/core/DefaultApplication.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.core;
+
+import java.net.URI;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default implementation of network control/management application descriptor.
+ */
+public class DefaultApplication implements Application {
+
+    private final ApplicationId appId;
+    private final Version version;
+    private final String description;
+    private final String origin;
+    private final Set<Permission> permissions;
+    private final Optional<URI> featuresRepo;
+    private final Set<String> features;
+
+    /**
+     * Creates a new application descriptor using the supplied data.
+     *
+     * @param appId        application identifier
+     * @param version      application version
+     * @param description  application description
+     * @param origin       origin company
+     * @param permissions  requested permissions
+     * @param featuresRepo optional features repo URI
+     * @param features     application features
+     */
+    public DefaultApplication(ApplicationId appId, Version version,
+                              String description, String origin,
+                              Set<Permission> permissions,
+                              Optional<URI> featuresRepo, Set<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");
+        this.origin = checkNotNull(origin, "Origin cannot be null");
+        this.permissions = checkNotNull(permissions, "Permissions cannot be null");
+        this.featuresRepo = checkNotNull(featuresRepo, "Features repo cannot be null");
+        this.features = checkNotNull(features, "Features cannot be null");
+        checkArgument(!features.isEmpty(), "There must be at least one feature");
+    }
+
+    @Override
+    public ApplicationId id() {
+        return appId;
+    }
+
+    @Override
+    public Version version() {
+        return version;
+    }
+
+    @Override
+    public String description() {
+        return description;
+    }
+
+    @Override
+    public String origin() {
+        return origin;
+    }
+
+    @Override
+    public Set<Permission> permissions() {
+        return permissions;
+    }
+
+    @Override
+    public Optional<URI> featuresRepo() {
+        return featuresRepo;
+    }
+
+    @Override
+    public Set<String> features() {
+        return features;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(appId, version, description, origin, permissions,
+                            featuresRepo, features);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final DefaultApplication other = (DefaultApplication) obj;
+        return Objects.equals(this.appId, other.appId) &&
+                Objects.equals(this.version, other.version) &&
+                Objects.equals(this.description, other.description) &&
+                Objects.equals(this.origin, other.origin) &&
+                Objects.equals(this.permissions, other.permissions) &&
+                Objects.equals(this.featuresRepo, other.featuresRepo) &&
+                Objects.equals(this.features, other.features);
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("appId", appId)
+                .add("version", version)
+                .add("description", description)
+                .add("origin", origin)
+                .add("permissions", permissions)
+                .add("featuresRepo", featuresRepo)
+                .add("features", features)
+                .toString();
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/core/DefaultApplicationId.java b/core/api/src/main/java/org/onosproject/core/DefaultApplicationId.java
index e6f448e..3a07b2b 100644
--- a/core/api/src/main/java/org/onosproject/core/DefaultApplicationId.java
+++ b/core/api/src/main/java/org/onosproject/core/DefaultApplicationId.java
@@ -18,6 +18,7 @@
 import java.util.Objects;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
 
 /**
  * Application identifier.
@@ -33,8 +34,9 @@
      * @param id   application identifier
      * @param name application name
      */
-    public DefaultApplicationId(Short id, String name) {
-        this.id = id;
+    public DefaultApplicationId(int id, String name) {
+        checkArgument(0 <= id && id <= Short.MAX_VALUE, "id is outside range");
+        this.id = (short) id;
         this.name = name;
     }
 
diff --git a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java b/core/api/src/main/java/org/onosproject/core/Permission.java
similarity index 75%
copy from core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
copy to core/api/src/main/java/org/onosproject/core/Permission.java
index e39533c..d32d059 100644
--- a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
+++ b/core/api/src/main/java/org/onosproject/core/Permission.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,10 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.json.impl;
+package org.onosproject.core;
 
 /**
- * Created by tom on 10/16/14.
+ * Representation of an application permission.
  */
-public class DeleteMe {
+public interface Permission {
+    // TODO: to be fleshed out
 }
diff --git a/core/api/src/test/java/org/onosproject/app/ApplicationAdminServiceAdapter.java b/core/api/src/test/java/org/onosproject/app/ApplicationAdminServiceAdapter.java
new file mode 100644
index 0000000..4e5ccf8
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/app/ApplicationAdminServiceAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.Permission;
+
+import java.io.InputStream;
+import java.util.Set;
+
+/**
+ * Adapter for testing against application admin service.
+ */
+public class ApplicationAdminServiceAdapter extends ApplicationServiceAdapter
+        implements ApplicationAdminService {
+    @Override
+    public Set<Application> getApplications() {
+        return null;
+    }
+
+    @Override
+    public Application getApplication(ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public ApplicationState getState(ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public Set<Permission> getPermissions(ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public void addListener(ApplicationListener listener) {
+    }
+
+    @Override
+    public void removeListener(ApplicationListener listener) {
+    }
+
+    @Override
+    public Application install(InputStream appDescStream) {
+        return null;
+    }
+
+    @Override
+    public void uninstall(ApplicationId appId) {
+    }
+
+    @Override
+    public void activate(ApplicationId appId) {
+    }
+
+    @Override
+    public void deactivate(ApplicationId appId) {
+    }
+
+    @Override
+    public void setPermissions(ApplicationId appId, Set<Permission> permissions) {
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/app/ApplicationEventTest.java b/core/api/src/test/java/org/onosproject/app/ApplicationEventTest.java
new file mode 100644
index 0000000..4a882c3
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/app/ApplicationEventTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.junit.Test;
+import org.onosproject.core.Application;
+import org.onosproject.core.DefaultApplication;
+import org.onosproject.event.AbstractEventTest;
+
+import java.util.Optional;
+
+import static org.onosproject.app.ApplicationEvent.Type.APP_ACTIVATED;
+import static org.onosproject.app.DefaultApplicationDescriptionTest.*;
+import static org.onosproject.core.DefaultApplicationTest.APP_ID;
+
+/**
+ * Test of the application event.
+ */
+public class ApplicationEventTest extends AbstractEventTest {
+
+    private Application createApp() {
+        return new DefaultApplication(APP_ID, VER, DESC, ORIGIN,
+                                      PERMS, Optional.of(FURL), FEATURES);
+    }
+
+    @Test
+    public void withoutTime() {
+        Application app = createApp();
+        ApplicationEvent event = new ApplicationEvent(APP_ACTIVATED, app, 123L);
+        validateEvent(event, APP_ACTIVATED, app, 123L);
+    }
+
+    @Test
+    public void withTime() {
+        Application app = createApp();
+        long before = System.currentTimeMillis();
+        ApplicationEvent event = new ApplicationEvent(APP_ACTIVATED, app);
+        long after = System.currentTimeMillis();
+        validateEvent(event, APP_ACTIVATED, app, before, after);
+    }
+
+}
\ No newline at end of file
diff --git a/core/api/src/test/java/org/onosproject/app/ApplicationExceptionTest.java b/core/api/src/test/java/org/onosproject/app/ApplicationExceptionTest.java
new file mode 100644
index 0000000..a0c7ef1
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/app/ApplicationExceptionTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onlab.junit.ExceptionTest;
+
+public class ApplicationExceptionTest extends ExceptionTest {
+
+    @Override
+    protected Exception getDefault() {
+        return new ApplicationException();
+    }
+
+    @Override
+    protected Exception getWithMessage() {
+        return new ApplicationException(MESSAGE);
+    }
+
+    @Override
+    protected Exception getWithMessageAndCause() {
+        return new ApplicationException(MESSAGE, CAUSE);
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/test/java/org/onosproject/app/ApplicationServiceAdapter.java b/core/api/src/test/java/org/onosproject/app/ApplicationServiceAdapter.java
new file mode 100644
index 0000000..ff1822a
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/app/ApplicationServiceAdapter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.Permission;
+
+import java.util.Set;
+
+/**
+ * Adapter for testing against application service.
+ */
+public class ApplicationServiceAdapter implements ApplicationService {
+    @Override
+    public Set<Application> getApplications() {
+        return null;
+    }
+
+    @Override
+    public ApplicationId getId(String name) {
+        return null;
+    }
+
+    @Override
+    public Application getApplication(ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public ApplicationState getState(ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public Set<Permission> getPermissions(ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public void addListener(ApplicationListener listener) {
+    }
+
+    @Override
+    public void removeListener(ApplicationListener listener) {
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/app/ApplicationStoreAdapter.java b/core/api/src/test/java/org/onosproject/app/ApplicationStoreAdapter.java
new file mode 100644
index 0000000..265c2a6
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/app/ApplicationStoreAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.Permission;
+import org.onosproject.store.AbstractStore;
+
+import java.io.InputStream;
+import java.util.Set;
+
+/**
+ * Adapter for application testing against application store.
+ */
+public class ApplicationStoreAdapter
+        extends AbstractStore<ApplicationEvent, ApplicationStoreDelegate>
+        implements ApplicationStore {
+    @Override
+    public Set<Application> getApplications() {
+        return null;
+    }
+
+    @Override
+    public ApplicationId getId(String name) {
+        return null;
+    }
+
+    @Override
+    public Application getApplication(ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public ApplicationState getState(ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public Application create(InputStream appDescStream) {
+        return null;
+    }
+
+    @Override
+    public void remove(ApplicationId appId) {
+    }
+
+    @Override
+    public void activate(ApplicationId appId) {
+    }
+
+    @Override
+    public void deactivate(ApplicationId appId) {
+    }
+
+    @Override
+    public Set<Permission> getPermissions(ApplicationId appId) {
+        return null;
+    }
+
+    @Override
+    public void setPermissions(ApplicationId appId, Set<Permission> permissions) {
+    }
+
+}
diff --git a/core/api/src/test/java/org/onosproject/app/DefaultApplicationDescriptionTest.java b/core/api/src/test/java/org/onosproject/app/DefaultApplicationDescriptionTest.java
new file mode 100644
index 0000000..7079427
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/app/DefaultApplicationDescriptionTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app;
+
+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.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Basic tests of the default app description.
+ */
+public class DefaultApplicationDescriptionTest {
+
+    public static final String APP_NAME = "org.foo.app";
+    public static final Version VER = Version.version(1, 2, "a", null);
+    public static final String DESC = "Awesome application from Circus";
+    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");
+
+    @Test
+    public void basics() {
+        ApplicationDescription app =
+                new DefaultApplicationDescription(APP_NAME, VER, DESC, ORIGIN,
+                                                  PERMS, FURL, FEATURES);
+        assertEquals("incorrect id", APP_NAME, app.name());
+        assertEquals("incorrect version", VER, app.version());
+        assertEquals("incorrect description", DESC, app.description());
+        assertEquals("incorrect origin", ORIGIN, app.origin());
+        assertEquals("incorrect permissions", PERMS, app.permissions());
+        assertEquals("incorrect features repo", FURL, app.featuresRepo().get());
+        assertEquals("incorrect features", FEATURES, app.features());
+        assertTrue("incorrect toString", app.toString().contains(APP_NAME));
+    }
+
+}
\ No newline at end of file
diff --git a/core/api/src/test/java/org/onosproject/core/ApplicationIdStoreAdapter.java b/core/api/src/test/java/org/onosproject/core/ApplicationIdStoreAdapter.java
new file mode 100644
index 0000000..c3d0f23
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/core/ApplicationIdStoreAdapter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.core;
+
+import java.util.Set;
+
+/**
+ * Adapter for testing against app id store.
+ */
+public class ApplicationIdStoreAdapter implements ApplicationIdStore {
+    @Override
+    public Set<ApplicationId> getAppIds() {
+        return null;
+    }
+
+    @Override
+    public ApplicationId getAppId(Short id) {
+        return null;
+    }
+
+    @Override
+    public ApplicationId getAppId(String name) {
+        return null;
+    }
+
+    @Override
+    public ApplicationId registerApplication(String identifier) {
+        return null;
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/core/DefaultApplicationTest.java b/core/api/src/test/java/org/onosproject/core/DefaultApplicationTest.java
new file mode 100644
index 0000000..ed0fe6d
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/core/DefaultApplicationTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.core;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.app.DefaultApplicationDescriptionTest.*;
+
+/**
+ * Basic tests of the default app descriptor.
+ */
+public class DefaultApplicationTest {
+
+    public static final ApplicationId APP_ID = new DefaultApplicationId(2, APP_NAME);
+
+    @Test
+    public void basics() {
+        Application app = new DefaultApplication(APP_ID, VER, DESC, ORIGIN,
+                                                 PERMS, Optional.of(FURL), FEATURES);
+        assertEquals("incorrect id", APP_ID, app.id());
+        assertEquals("incorrect version", VER, app.version());
+        assertEquals("incorrect description", DESC, app.description());
+        assertEquals("incorrect origin", ORIGIN, app.origin());
+        assertEquals("incorrect permissions", PERMS, app.permissions());
+        assertEquals("incorrect features repo", FURL, app.featuresRepo().get());
+        assertEquals("incorrect features", FEATURES, app.features());
+        assertTrue("incorrect toString", app.toString().contains(APP_NAME));
+    }
+
+    @Test
+    public void testEquality() {
+        Application a1 = new DefaultApplication(APP_ID, VER, DESC, ORIGIN,
+                                                PERMS, Optional.of(FURL), FEATURES);
+        Application a2 = new DefaultApplication(APP_ID, VER, DESC, ORIGIN,
+                                                PERMS, Optional.of(FURL), FEATURES);
+        Application a3 = new DefaultApplication(APP_ID, VER, DESC, ORIGIN,
+                                                PERMS, Optional.empty(), FEATURES);
+        Application a4 = new DefaultApplication(APP_ID, VER, DESC, ORIGIN + "asd",
+                                                PERMS, Optional.of(FURL), FEATURES);
+        new EqualsTester().addEqualityGroup(a1, a2)
+                .addEqualityGroup(a3).addEqualityGroup(a4).testEquals();
+    }
+
+}
\ No newline at end of file
diff --git a/core/api/src/test/java/org/onosproject/core/UnavailableIdExceptionTest.java b/core/api/src/test/java/org/onosproject/core/UnavailableIdExceptionTest.java
new file mode 100644
index 0000000..ac565d8
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/core/UnavailableIdExceptionTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.core;
+
+import org.onlab.junit.ExceptionTest;
+
+public class UnavailableIdExceptionTest extends ExceptionTest {
+
+    @Override
+    protected Exception getDefault() {
+        return new UnavailableIdException();
+    }
+
+    @Override
+    protected Exception getWithMessage() {
+        return new UnavailableIdException(MESSAGE);
+    }
+
+    @Override
+    protected Exception getWithMessageAndCause() {
+        return new UnavailableIdException(MESSAGE, CAUSE);
+    }
+}
\ No newline at end of file
diff --git a/core/api/src/test/java/org/onosproject/net/intent/IntentOperationsTest.java b/core/api/src/test/java/org/onosproject/net/intent/IntentOperationsTest.java
index ec0ddae..484a44a 100644
--- a/core/api/src/test/java/org/onosproject/net/intent/IntentOperationsTest.java
+++ b/core/api/src/test/java/org/onosproject/net/intent/IntentOperationsTest.java
@@ -45,7 +45,7 @@
     final TrafficSelector selector = new IntentTestsMocks.MockSelector();
     final IntentTestsMocks.MockTreatment treatment = new IntentTestsMocks.MockTreatment();
 
-    private final ApplicationId appId = new DefaultApplicationId((short) 1, "IntentOperationsTest");
+    private final ApplicationId appId = new DefaultApplicationId(1, "IntentOperationsTest");
 
     private Intent intent;
     protected IdGenerator idGenerator = new MockIdGenerator();
diff --git a/core/json/pom.xml b/core/common/pom.xml
similarity index 84%
rename from core/json/pom.xml
rename to core/common/pom.xml
index d9a84c1..8c5b80f 100644
--- a/core/json/pom.xml
+++ b/core/common/pom.xml
@@ -26,16 +26,17 @@
         <relativePath>../pom.xml</relativePath>
     </parent>
 
-    <artifactId>onos-json</artifactId>
+    <artifactId>onos-core-common</artifactId>
     <packaging>bundle</packaging>
 
-    <description>ONOS JSON encode/decode facilities</description>
+    <description>ONOS utilities common to the core modules</description>
 
     <dependencies>
         <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-api</artifactId>
         </dependency>
+
         <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-api</artifactId>
@@ -44,13 +45,17 @@
         </dependency>
 
         <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-core-trivial</artifactId>
-            <version>${project.version}</version>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
             <scope>test</scope>
         </dependency>
 
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
diff --git a/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java b/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
new file mode 100644
index 0000000..90e6005
--- /dev/null
+++ b/core/common/src/main/java/org/onosproject/common/app/ApplicationArchive.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.common.app;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Files;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.onlab.util.Tools;
+import org.onosproject.app.ApplicationDescription;
+import org.onosproject.app.ApplicationEvent;
+import org.onosproject.app.ApplicationException;
+import org.onosproject.app.ApplicationStoreDelegate;
+import org.onosproject.app.DefaultApplicationDescription;
+import org.onosproject.core.Permission;
+import org.onosproject.core.Version;
+import org.onosproject.store.AbstractStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import static com.google.common.io.ByteStreams.toByteArray;
+import static com.google.common.io.Files.createParentDirs;
+import static com.google.common.io.Files.write;
+
+/**
+ * Facility for reading application archive stream and managing application
+ * directory structure.
+ */
+public class ApplicationArchive
+        extends AbstractStore<ApplicationEvent, ApplicationStoreDelegate> {
+
+    private static final String NAME = "[@name]";
+    private static final String ORIGIN = "[@origin]";
+    private static final String VERSION = "[@version]";
+    private static final String FEATURES_REPO = "[@featuresRepo]";
+    private static final String FEATURES = "[@features]";
+    private static final String DESCRIPTION = "description";
+
+    private static Logger log = LoggerFactory.getLogger(ApplicationArchive.class);
+    private static final String APP_XML = "app.xml";
+    private static final String APPS_ROOT = "data/apps/";
+
+    private File appsDir = new File(APPS_ROOT);
+
+    /**
+     * Sets the root directory where application artifacts are kept.
+     *
+     * @param appsRoot top-level applications directory path
+     */
+    protected void setAppsRoot(String appsRoot) {
+        this.appsDir = new File(appsRoot);
+    }
+
+    /**
+     * Returns the root directory where application artifacts are kept.
+     *
+     * @return top-level applications directory path
+     */
+    protected String getAppsRoot() {
+        return appsDir.getPath();
+    }
+
+    /**
+     * Returns the set of installed application names.
+     *
+     * @return installed application names
+     */
+    public Set<String> getApplicationNames() {
+        ImmutableSet.Builder<String> names = ImmutableSet.builder();
+        File[] files = appsDir.listFiles(File::isDirectory);
+        if (files != null) {
+            for (File file : files) {
+                names.add(file.getName());
+            }
+        }
+        return names.build();
+    }
+
+    /**
+     * Loads the application descriptor from the specified application archive
+     * stream and saves the stream in the appropriate application archive
+     * directory.
+     *
+     * @param appName application name
+     * @return application descriptor
+     * @throws org.onosproject.app.ApplicationException if unable to read application description
+     */
+    public ApplicationDescription getApplicationDescription(String appName) {
+        try {
+            return loadAppDescription(new XMLConfiguration(appFile(appName, APP_XML)));
+        } catch (Exception e) {
+            throw new ApplicationException("Unable to get app description", e);
+        }
+    }
+
+    /**
+     * Loads the application descriptor from the specified application archive
+     * stream and saves the stream in the appropriate application archive
+     * directory.
+     *
+     * @param stream application archive stream
+     * @return application descriptor
+     * @throws org.onosproject.app.ApplicationException if unable to read the
+     *                                                  archive stream or store
+     *                                                  the application archive
+     */
+    public ApplicationDescription saveApplication(InputStream stream) {
+        try (InputStream ais = stream) {
+            byte[] cache = toByteArray(ais);
+            InputStream bis = new ByteArrayInputStream(cache);
+            ApplicationDescription desc = parseAppDescription(bis);
+            bis.reset();
+
+            expandApplication(bis, desc);
+            bis.reset();
+
+            saveApplication(bis, desc);
+            installArtifacts(desc);
+            return desc;
+        } catch (IOException e) {
+            throw new ApplicationException("Unable to save application", e);
+        }
+    }
+
+    /**
+     * Purges the application archive directory.
+     *
+     * @param appName application name
+     */
+    public void purgeApplication(String appName) {
+        try {
+            Tools.removeDirectory(new File(appsDir, appName));
+        } catch (IOException e) {
+            throw new ApplicationException("Unable to purge application " + appName, e);
+        }
+    }
+
+    /**
+     * Returns application archive stream for the specified application.
+     *
+     * @param appName application name
+     * @return application archive stream
+     */
+    public InputStream getApplicationInputStream(String appName) {
+        try {
+            return new FileInputStream(appFile(appName, appName + ".zip"));
+        } catch (FileNotFoundException e) {
+            throw new ApplicationException("Application " + appName + " not found");
+        }
+    }
+
+    // Scans the specified ZIP stream for app.xml entry and parses it producing
+    // an application descriptor.
+    private ApplicationDescription parseAppDescription(InputStream stream)
+            throws IOException {
+        try (ZipInputStream zis = new ZipInputStream(stream)) {
+            ZipEntry entry;
+            while ((entry = zis.getNextEntry()) != null) {
+                if (entry.getName().equals(APP_XML)) {
+                    byte[] data = new byte[(int) entry.getSize()];
+                    ByteStreams.readFully(zis, data);
+                    XMLConfiguration cfg = new XMLConfiguration();
+                    try {
+                        cfg.load(new ByteArrayInputStream(data));
+                        return loadAppDescription(cfg);
+                    } catch (ConfigurationException e) {
+                        throw new IOException("Unable to parse " + APP_XML, e);
+                    }
+                }
+                zis.closeEntry();
+            }
+        }
+        throw new IOException("Unable to locate " + APP_XML);
+    }
+
+    private ApplicationDescription loadAppDescription(XMLConfiguration cfg) {
+        cfg.setAttributeSplittingDisabled(true);
+        String name = cfg.getString(NAME);
+        Version version = Version.version(cfg.getString(VERSION));
+        String desc = cfg.getString(DESCRIPTION);
+        String origin = cfg.getString(ORIGIN);
+        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(","));
+
+        return new DefaultApplicationDescription(name, version, desc, origin,
+                                                 perms, featuresRepo, features);
+    }
+
+    // Expands the specified ZIP stream into app-specific directory.
+    private void expandApplication(InputStream stream, ApplicationDescription desc)
+            throws IOException {
+        ZipInputStream zis = new ZipInputStream(stream);
+        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();
+
+            File file = new File(appDir, entry.getName());
+            createParentDirs(file);
+            write(data, file);
+        }
+        zis.close();
+    }
+
+    // Saves the specified ZIP stream into a file under app-specific directory.
+    private void saveApplication(InputStream stream, ApplicationDescription desc)
+            throws IOException {
+        Files.write(toByteArray(stream), appFile(desc.name(), desc.name() + ".zip"));
+    }
+
+    // Installs application artifacts into M2 repository.
+    private void installArtifacts(ApplicationDescription desc) {
+        // FIXME: implement M2 repository copy
+    }
+
+    protected boolean setActive(String appName) {
+        try {
+            return appFile(appName, "active").createNewFile();
+        } catch (IOException e) {
+            throw new ApplicationException("Unable to mark app as active", e);
+        }
+    }
+
+    protected boolean clearActive(String appName) {
+        return appFile(appName, "active").delete();
+    }
+
+    protected boolean isActive(String appName) {
+        return appFile(appName, "active").exists();
+    }
+
+
+    // Returns the name of the file located under the specified app directory.
+    private File appFile(String appName, String fileName) {
+        return new File(new File(appsDir, appName), fileName);
+    }
+
+}
diff --git a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java b/core/common/src/main/java/org/onosproject/common/app/package-info.java
similarity index 78%
copy from core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
copy to core/common/src/main/java/org/onosproject/common/app/package-info.java
index e39533c..898bad7 100644
--- a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
+++ b/core/common/src/main/java/org/onosproject/common/app/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,10 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.json.impl;
 
 /**
- * Created by tom on 10/16/14.
+ * Common facilities for construction of application management subsystem.
  */
-public class DeleteMe {
-}
+package org.onosproject.common.app;
\ No newline at end of file
diff --git a/core/common/src/test/java/org/onosproject/common/app/ApplicationArchiveTest.java b/core/common/src/test/java/org/onosproject/common/app/ApplicationArchiveTest.java
new file mode 100644
index 0000000..c1b8e82
--- /dev/null
+++ b/core/common/src/test/java/org/onosproject/common/app/ApplicationArchiveTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.common.app;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteStreams;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.app.ApplicationDescription;
+import org.onosproject.app.ApplicationException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Set;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.app.DefaultApplicationDescriptionTest.*;
+
+public class ApplicationArchiveTest {
+
+    static final String ROOT = "/tmp/app-junit";
+
+    private ApplicationArchive aar = new ApplicationArchive();
+
+    @Before
+    public void setUp() {
+        aar.setAppsRoot(ROOT);
+    }
+
+    private void validate(ApplicationDescription app) {
+        assertEquals("incorrect name", APP_NAME, app.name());
+        assertEquals("incorrect version", VER, app.version());
+        assertEquals("incorrect origin", ORIGIN, app.origin());
+
+        assertEquals("incorrect description", DESC, app.description());
+        assertEquals("incorrect features URI", FURL, app.featuresRepo().get());
+        assertEquals("incorrect permissions", PERMS, app.permissions());
+        assertEquals("incorrect features", FEATURES, app.features());
+    }
+
+    @Test
+    public void saveApp() throws IOException {
+        InputStream stream = getClass().getResourceAsStream("app.zip");
+        ApplicationDescription app = aar.saveApplication(stream);
+        validate(app);
+    }
+
+    @Test
+    public void loadApp() throws IOException {
+        saveApp();
+        ApplicationDescription app = aar.getApplicationDescription(APP_NAME);
+        validate(app);
+    }
+
+    @Test
+    public void getAppNames() throws IOException {
+        saveApp();
+        Set<String> names = aar.getApplicationNames();
+        assertEquals("incorrect names", ImmutableSet.of(APP_NAME), names);
+    }
+
+    @Test
+    public void purgeApp() throws IOException {
+        saveApp();
+        aar.purgeApplication(APP_NAME);
+        assertEquals("incorrect names", ImmutableSet.of(), aar.getApplicationNames());
+    }
+
+    @Test
+    public void getAppStream() throws IOException {
+        saveApp();
+        InputStream stream = aar.getApplicationInputStream(APP_NAME);
+        byte[] orig = ByteStreams.toByteArray(getClass().getResourceAsStream("app.zip"));
+        byte[] loaded = ByteStreams.toByteArray(stream);
+        assertArrayEquals("incorrect stream", orig, loaded);
+    }
+
+    @Test(expected = ApplicationException.class)
+    public void getBadAppDesc() throws IOException {
+        aar.getApplicationDescription("org.foo.BAD");
+    }
+
+    @Test(expected = ApplicationException.class)
+    public void getBadAppStream() throws IOException {
+        aar.getApplicationInputStream("org.foo.BAD");
+    }
+
+}
\ No newline at end of file
diff --git a/core/common/src/test/resources/org/onosproject/common/app/app.xml b/core/common/src/test/resources/org/onosproject/common/app/app.xml
new file mode 100644
index 0000000..3b8bc62
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/common/app/app.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright 2015 Open Networking Laboratory
+  ~
+  ~ 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.
+  -->
+<app name="org.foo.app" origin="Circus" version="1.2.a"
+        featuresRepo="mvn:org.foo-features/1.2a/xml/features"
+        features="foo,bar">
+    <description>Awesome application from Circus, Inc.</description>
+</app>
diff --git a/core/common/src/test/resources/org/onosproject/common/app/app.zip b/core/common/src/test/resources/org/onosproject/common/app/app.zip
new file mode 100644
index 0000000..07705e7
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/common/app/app.zip
Binary files differ
diff --git a/core/json/src/main/java/org/onosproject/json/impl/package-info.java b/core/json/src/main/java/org/onosproject/json/impl/package-info.java
deleted file mode 100644
index 11fa30d..0000000
--- a/core/json/src/main/java/org/onosproject/json/impl/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2014 Open Networking Laboratory
- *
- * 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.
- */
-
-/**
- * Implementation of JSON codec factory and of the builtin codecs.
- */
-package org.onosproject.json.impl;
diff --git a/core/net/pom.xml b/core/net/pom.xml
index 4150ec0..2bc6a60 100644
--- a/core/net/pom.xml
+++ b/core/net/pom.xml
@@ -52,6 +52,14 @@
         </dependency>
 
         <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-common</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.easymock</groupId>
             <artifactId>easymock</artifactId>
             <scope>test</scope>
@@ -66,6 +74,12 @@
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.karaf.features</groupId>
+            <artifactId>org.apache.karaf.features.core</artifactId>
+            <version>3.0.2</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java b/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java
new file mode 100644
index 0000000..08c91a4
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.karaf.features.FeaturesService;
+import org.onosproject.app.ApplicationAdminService;
+import org.onosproject.app.ApplicationEvent;
+import org.onosproject.app.ApplicationListener;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.app.ApplicationState;
+import org.onosproject.app.ApplicationStore;
+import org.onosproject.app.ApplicationStoreDelegate;
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.Permission;
+import org.onosproject.event.AbstractListenerRegistry;
+import org.onosproject.event.EventDeliveryService;
+import org.slf4j.Logger;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.app.ApplicationEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the application management service.
+ */
+@Component(immediate = true)
+@Service
+public class ApplicationManager implements ApplicationService, ApplicationAdminService {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String APP_ID_NULL = "Application ID cannot be null";
+
+    protected final AbstractListenerRegistry<ApplicationEvent, ApplicationListener>
+            listenerRegistry = new AbstractListenerRegistry<>();
+
+    private final ApplicationStoreDelegate delegate = new InternalStoreDelegate();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ApplicationStore store;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FeaturesService featuresService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected EventDeliveryService eventDispatcher;
+
+    @Activate
+    public void activate() {
+        store.setDelegate(delegate);
+        eventDispatcher.addSink(ApplicationEvent.class, listenerRegistry);
+        log.info("Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        store.unsetDelegate(delegate);
+        eventDispatcher.removeSink(ApplicationEvent.class);
+        log.info("Stopped");
+    }
+
+    @Override
+    public Set<Application> getApplications() {
+        return store.getApplications();
+    }
+
+    @Override
+    public ApplicationId getId(String name) {
+        checkNotNull(name, "Name cannot be null");
+        return store.getId(name);
+    }
+
+    @Override
+    public Application getApplication(ApplicationId appId) {
+        checkNotNull(appId, APP_ID_NULL);
+        return store.getApplication(appId);
+    }
+
+    @Override
+    public ApplicationState getState(ApplicationId appId) {
+        checkNotNull(appId, APP_ID_NULL);
+        return store.getState(appId);
+    }
+
+    @Override
+    public Set<Permission> getPermissions(ApplicationId appId) {
+        checkNotNull(appId, APP_ID_NULL);
+        return store.getPermissions(appId);
+    }
+
+    @Override
+    public Application install(InputStream appDescStream) {
+        checkNotNull(appDescStream, "Application archive stream cannot be null");
+        return store.create(appDescStream);
+    }
+
+    @Override
+    public void uninstall(ApplicationId appId) {
+        checkNotNull(appId, APP_ID_NULL);
+        try {
+            store.remove(appId);
+        } catch (Exception e) {
+            log.warn("Unable to purge application directory for {}", appId.name());
+        }
+    }
+
+    @Override
+    public void activate(ApplicationId appId) {
+        checkNotNull(appId, APP_ID_NULL);
+        store.activate(appId);
+    }
+
+    @Override
+    public void deactivate(ApplicationId appId) {
+        checkNotNull(appId, APP_ID_NULL);
+        store.deactivate(appId);
+    }
+
+    @Override
+    public void setPermissions(ApplicationId appId, Set<Permission> permissions) {
+        checkNotNull(appId, APP_ID_NULL);
+        checkNotNull(permissions, "Permissions cannot be null");
+        store.setPermissions(appId, permissions);
+    }
+
+    @Override
+    public void addListener(ApplicationListener listener) {
+        listenerRegistry.addListener(listener);
+    }
+
+    @Override
+    public void removeListener(ApplicationListener listener) {
+        listenerRegistry.removeListener(listener);
+    }
+
+    private class InternalStoreDelegate implements ApplicationStoreDelegate {
+        @Override
+        public void notify(ApplicationEvent event) {
+            ApplicationEvent.Type type = event.type();
+            Application app = event.subject();
+            try {
+                if (type == APP_ACTIVATED) {
+                    installAppFeatures(app);
+                    log.info("Application {} has been activated", app.id().name());
+
+                } else if (type == APP_DEACTIVATED) {
+                    uninstallAppFeatures(app);
+                    log.info("Application {} has been deactivated", app.id().name());
+
+                } else if (type == APP_INSTALLED) {
+                    installAppArtifacts(app);
+                    log.info("Application {} has been installed", app.id().name());
+
+                } else if (type == APP_UNINSTALLED) {
+                    uninstallAppFeatures(app);
+                    uninstallAppArtifacts(app);
+                    log.info("Application {} has been uninstalled", app.id().name());
+
+                }
+                eventDispatcher.post(event);
+
+            } catch (Exception e) {
+                log.warn("Unable to perform operation on application " + app.id().name(), e);
+            }
+        }
+    }
+
+    private void installAppArtifacts(Application app) throws Exception {
+        if (app.featuresRepo().isPresent()) {
+            featuresService.addRepository(app.featuresRepo().get());
+        }
+    }
+
+    private void uninstallAppArtifacts(Application app) throws Exception {
+        if (app.featuresRepo().isPresent()) {
+            featuresService.removeRepository(app.featuresRepo().get());
+        }
+    }
+
+    private void installAppFeatures(Application app) throws Exception {
+        for (String name : app.features()) {
+            featuresService.installFeature(name);
+        }
+    }
+
+    private void uninstallAppFeatures(Application app) throws Exception {
+        for (String name : app.features()) {
+            featuresService.uninstallFeature(name);
+        }
+    }
+
+}
diff --git a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java b/core/net/src/main/java/org/onosproject/app/impl/package-info.java
similarity index 80%
rename from core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
rename to core/net/src/main/java/org/onosproject/app/impl/package-info.java
index e39533c..d5f3037 100644
--- a/core/json/src/main/java/org/onosproject/json/impl/DeleteMe.java
+++ b/core/net/src/main/java/org/onosproject/app/impl/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Open Networking Laboratory
+ * Copyright 2015 Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,10 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.json.impl;
 
 /**
- * Created by tom on 10/16/14.
+ * Subsystem for managing applications.
  */
-public class DeleteMe {
-}
+package org.onosproject.app.impl;
\ No newline at end of file
diff --git a/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java b/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java
new file mode 100644
index 0000000..3477207
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/app/impl/ApplicationManagerTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.app.ApplicationEvent;
+import org.onosproject.app.ApplicationListener;
+import org.onosproject.app.ApplicationState;
+import org.onosproject.app.ApplicationStoreAdapter;
+import org.onosproject.common.app.ApplicationArchive;
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplication;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.event.impl.TestEventDispatcher;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.app.ApplicationEvent.Type.*;
+import static org.onosproject.app.ApplicationState.ACTIVE;
+import static org.onosproject.app.ApplicationState.INSTALLED;
+import static org.onosproject.app.DefaultApplicationDescriptionTest.*;
+
+/**
+ * Test of the application manager implementation.
+ */
+public class ApplicationManagerTest {
+
+    public static final DefaultApplicationId APP_ID = new DefaultApplicationId(1, APP_NAME);
+
+    private ApplicationManager mgr = new ApplicationManager();
+    private ApplicationListener listener = new TestListener();
+
+    @Before
+    public void setUp() {
+        mgr.eventDispatcher = new TestEventDispatcher();
+        mgr.featuresService = new TestFeaturesService();
+        mgr.store = new TestStore();
+        mgr.activate();
+        mgr.addListener(listener);
+    }
+
+    @After
+    public void tearDown() {
+        mgr.removeListener(listener);
+        mgr.deactivate();
+    }
+
+    private void validate(Application app) {
+        assertEquals("incorrect name", APP_NAME, app.id().name());
+        assertEquals("incorrect version", VER, app.version());
+        assertEquals("incorrect origin", ORIGIN, app.origin());
+
+        assertEquals("incorrect description", DESC, app.description());
+        assertEquals("incorrect features URI", FURL, app.featuresRepo().get());
+        assertEquals("incorrect features", FEATURES, app.features());
+    }
+
+    @Test
+    public void install() {
+        InputStream stream = ApplicationArchive.class.getResourceAsStream("app.zip");
+        Application app = mgr.install(stream);
+        validate(app);
+        assertEquals("incorrect features URI used", app.featuresRepo().get(),
+                     ((TestFeaturesService) mgr.featuresService).uri);
+        assertEquals("incorrect app count", 1, mgr.getApplications().size());
+        assertEquals("incorrect app", app, mgr.getApplication(APP_ID));
+        assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID));
+    }
+
+    @Test
+    public void uninstall() {
+        install();
+        mgr.uninstall(APP_ID);
+        assertEquals("incorrect app count", 0, mgr.getApplications().size());
+    }
+
+    @Test
+    public void activate() {
+        install();
+        mgr.activate(APP_ID);
+        assertEquals("incorrect app state", ACTIVE, mgr.getState(APP_ID));
+    }
+
+    @Test
+    public void deactivate() {
+        activate();
+        mgr.deactivate(APP_ID);
+        assertEquals("incorrect app state", INSTALLED, mgr.getState(APP_ID));
+    }
+
+
+    private class TestListener implements ApplicationListener {
+        private ApplicationEvent event;
+
+        @Override
+        public void event(ApplicationEvent event) {
+            this.event = event;
+        }
+    }
+
+    private class TestStore extends ApplicationStoreAdapter {
+
+        private Application app;
+        private ApplicationState state;
+
+        @Override
+        public Application create(InputStream appDescStream) {
+            app = new DefaultApplication(APP_ID, VER, DESC, ORIGIN, PERMS,
+                                         Optional.of(FURL), FEATURES);
+            state = INSTALLED;
+            delegate.notify(new ApplicationEvent(APP_INSTALLED, app));
+            return app;
+        }
+
+        @Override
+        public Set<Application> getApplications() {
+            return app != null ? ImmutableSet.of(app) : ImmutableSet.of();
+        }
+
+        @Override
+        public Application getApplication(ApplicationId appId) {
+            return app;
+        }
+
+        @Override
+        public void remove(ApplicationId appId) {
+            delegate.notify(new ApplicationEvent(APP_UNINSTALLED, app));
+            app = null;
+            state = null;
+        }
+
+        @Override
+        public ApplicationState getState(ApplicationId appId) {
+            return state;
+        }
+
+        @Override
+        public void activate(ApplicationId appId) {
+            state = ApplicationState.ACTIVE;
+            delegate.notify(new ApplicationEvent(APP_ACTIVATED, app));
+        }
+
+        @Override
+        public void deactivate(ApplicationId appId) {
+            state = INSTALLED;
+            delegate.notify(new ApplicationEvent(APP_DEACTIVATED, app));
+        }
+    }
+
+    private class TestFeaturesService extends FeaturesServiceAdapter {
+        private URI uri;
+        private Set<String> features = new HashSet<>();
+
+        @Override
+        public void addRepository(URI uri) throws Exception {
+            this.uri = uri;
+        }
+
+        @Override
+        public void removeRepository(URI uri) throws Exception {
+            this.uri = null;
+        }
+
+        @Override
+        public void installFeature(String name) throws Exception {
+            features.add(name);
+        }
+
+        @Override
+        public void uninstallFeature(String name) throws Exception {
+            features.remove(name);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/core/net/src/test/java/org/onosproject/app/impl/FeaturesServiceAdapter.java b/core/net/src/test/java/org/onosproject/app/impl/FeaturesServiceAdapter.java
new file mode 100644
index 0000000..f527bd4
--- /dev/null
+++ b/core/net/src/test/java/org/onosproject/app/impl/FeaturesServiceAdapter.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.app.impl;
+
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.Repository;
+
+import java.net.URI;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * Adapter for testing against Apache Karaf feature service.
+ */
+public class FeaturesServiceAdapter implements org.apache.karaf.features.FeaturesService {
+    @Override
+    public void validateRepository(URI uri) throws Exception {
+
+    }
+
+    @Override
+    public void addRepository(URI uri) throws Exception {
+
+    }
+
+    @Override
+    public void addRepository(URI uri, boolean install) throws Exception {
+
+    }
+
+    @Override
+    public void removeRepository(URI uri) throws Exception {
+
+    }
+
+    @Override
+    public void removeRepository(URI uri, boolean uninstall) throws Exception {
+
+    }
+
+    @Override
+    public void restoreRepository(URI uri) throws Exception {
+
+    }
+
+    @Override
+    public Repository[] listRepositories() {
+        return new Repository[0];
+    }
+
+    @Override
+    public Repository getRepository(String repoName) {
+        return null;
+    }
+
+    @Override
+    public void installFeature(String name) throws Exception {
+
+    }
+
+    @Override
+    public void installFeature(String name, EnumSet<Option> options) throws Exception {
+
+    }
+
+    @Override
+    public void installFeature(String name, String version) throws Exception {
+
+    }
+
+    @Override
+    public void installFeature(String name, String version, EnumSet<Option> options) throws Exception {
+
+    }
+
+    @Override
+    public void installFeature(Feature f, EnumSet<Option> options) throws Exception {
+
+    }
+
+    @Override
+    public void installFeatures(Set<Feature> features, EnumSet<Option> options) throws Exception {
+
+    }
+
+    @Override
+    public void uninstallFeature(String name, EnumSet<Option> options) throws Exception {
+
+    }
+
+    @Override
+    public void uninstallFeature(String name) throws Exception {
+
+    }
+
+    @Override
+    public void uninstallFeature(String name, String version, EnumSet<Option> options) throws Exception {
+
+    }
+
+    @Override
+    public void uninstallFeature(String name, String version) throws Exception {
+
+    }
+
+    @Override
+    public Feature[] listFeatures() throws Exception {
+        return new Feature[0];
+    }
+
+    @Override
+    public Feature[] listInstalledFeatures() {
+        return new Feature[0];
+    }
+
+    @Override
+    public boolean isInstalled(Feature f) {
+        return false;
+    }
+
+    @Override
+    public Feature getFeature(String name, String version) throws Exception {
+        return null;
+    }
+
+    @Override
+    public Feature getFeature(String name) throws Exception {
+        return null;
+    }
+
+    @Override
+    public void refreshRepository(URI uri) throws Exception {
+
+    }
+}
diff --git a/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java b/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
index 974af66..5a79b43 100644
--- a/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
+++ b/core/net/src/test/java/org/onosproject/net/flow/impl/FlowRuleManagerTest.java
@@ -110,7 +110,7 @@
         mgr.addListener(listener);
         provider = new TestProvider(PID);
         providerService = registry.register(provider);
-        appId = new TestApplicationId((short) 0, "FlowRuleManagerTest");
+        appId = new TestApplicationId(0, "FlowRuleManagerTest");
         assertTrue("provider should be registered",
                    registry.getProviders().contains(provider.id()));
     }
@@ -639,8 +639,7 @@
     }
 
     public class TestApplicationId extends DefaultApplicationId {
-
-        public TestApplicationId(short id, String name) {
+        public TestApplicationId(int id, String name) {
             super(id, name);
         }
     }
diff --git a/core/pom.xml b/core/pom.xml
index c826b3b..5e2058b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -33,9 +33,9 @@
 
     <modules>
         <module>api</module>
+        <module>common</module>
         <module>net</module>
         <module>store</module>
-        <module>json</module>
     </modules>
 
     <dependencies>
diff --git a/core/store/dist/src/main/java/org/onosproject/store/core/impl/DistributedApplicationIdStore.java b/core/store/dist/src/main/java/org/onosproject/store/core/impl/DistributedApplicationIdStore.java
index f55ea54..186fb4e 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/core/impl/DistributedApplicationIdStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/core/impl/DistributedApplicationIdStore.java
@@ -103,6 +103,11 @@
         return appId;
     }
 
+    @Override
+    public ApplicationId getAppId(String name) {
+        return appIdsByName.get(name);
+    }
+
     private void primeAppIds() {
         for (DefaultApplicationId appId : appIdsByName.values()) {
             appIds.put(appId.id(), appId);
@@ -113,7 +118,7 @@
     public ApplicationId registerApplication(String name) {
         DefaultApplicationId appId = appIdsByName.get(name);
         if (appId == null) {
-            short id = (short) lastAppId.getAndIncrement();
+            int id = (int) lastAppId.getAndIncrement();
             appId = putIfAbsent(appIdsByName, name,
                                 new DefaultApplicationId(id, name));
         }
diff --git a/core/store/pom.xml b/core/store/pom.xml
index 334832f..e5078b9 100644
--- a/core/store/pom.xml
+++ b/core/store/pom.xml
@@ -44,6 +44,14 @@
         </dependency>
 
         <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-common</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
diff --git a/core/store/trivial/pom.xml b/core/store/trivial/pom.xml
index 5e82ee8..2842ead 100644
--- a/core/store/trivial/pom.xml
+++ b/core/store/trivial/pom.xml
@@ -38,6 +38,11 @@
         </dependency>
 
         <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-common</artifactId>
+        </dependency>
+
+        <dependency>
           <groupId>org.onosproject</groupId>
           <artifactId>onos-api</artifactId>
           <classifier>tests</classifier>
diff --git a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleApplicationIdStore.java b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleApplicationIdStore.java
index 34abd44..d6a9310 100644
--- a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleApplicationIdStore.java
+++ b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleApplicationIdStore.java
@@ -51,6 +51,11 @@
     }
 
     @Override
+    public ApplicationId getAppId(String name) {
+        return appIdsByName.get(name);
+    }
+
+    @Override
     public ApplicationId registerApplication(String name) {
         DefaultApplicationId appId = appIdsByName.get(name);
         if (appId == null) {
diff --git a/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleApplicationStore.java b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleApplicationStore.java
new file mode 100644
index 0000000..cecf5fd
--- /dev/null
+++ b/core/store/trivial/src/main/java/org/onosproject/store/trivial/impl/SimpleApplicationStore.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.store.trivial.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.app.ApplicationDescription;
+import org.onosproject.app.ApplicationEvent;
+import org.onosproject.app.ApplicationState;
+import org.onosproject.app.ApplicationStore;
+import org.onosproject.common.app.ApplicationArchive;
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.ApplicationIdStore;
+import org.onosproject.core.DefaultApplication;
+import org.onosproject.core.Permission;
+import org.slf4j.Logger;
+
+import java.io.InputStream;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.onosproject.app.ApplicationEvent.Type.*;
+import static org.onosproject.app.ApplicationState.ACTIVE;
+import static org.onosproject.app.ApplicationState.INSTALLED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages inventory of network control applications.
+ */
+@Component(immediate = true)
+@Service
+public class SimpleApplicationStore extends ApplicationArchive implements ApplicationStore {
+
+    private final Logger log = getLogger(getClass());
+
+    // App inventory & states
+    private final ConcurrentMap<ApplicationId, DefaultApplication> apps = new ConcurrentHashMap<>();
+    private final ConcurrentMap<ApplicationId, ApplicationState> states = new ConcurrentHashMap<>();
+    private final ConcurrentMap<ApplicationId, Set<Permission>> permissions = new ConcurrentHashMap<>();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ApplicationIdStore idStore;
+
+    @Activate
+    public void activate() {
+        loadFromDisk();
+        log.info("Started");
+    }
+
+    private void loadFromDisk() {
+        for (String name : getApplicationNames()) {
+            ApplicationId appId = idStore.registerApplication(name);
+            ApplicationDescription appDesc = getApplicationDescription(name);
+            DefaultApplication app =
+                    new DefaultApplication(appId, appDesc.version(),
+                                           appDesc.description(), appDesc.origin(),
+                                           appDesc.permissions(),
+                                           appDesc.featuresRepo(), appDesc.features());
+            apps.put(appId, app);
+            states.put(appId, isActive(name) ? INSTALLED : ACTIVE);
+            // load app permissions
+        }
+    }
+
+    @Deactivate
+    public void deactivate() {
+        apps.clear();
+        states.clear();
+        permissions.clear();
+        log.info("Stopped");
+    }
+
+    @Override
+    public Set<Application> getApplications() {
+        return ImmutableSet.copyOf(apps.values());
+    }
+
+    @Override
+    public ApplicationId getId(String name) {
+        return idStore.getAppId(name);
+    }
+
+    @Override
+    public Application getApplication(ApplicationId appId) {
+        return apps.get(appId);
+    }
+
+    @Override
+    public ApplicationState getState(ApplicationId appId) {
+        return states.get(appId);
+    }
+
+    @Override
+    public Application create(InputStream appDescStream) {
+        ApplicationDescription appDesc = saveApplication(appDescStream);
+        ApplicationId appId = idStore.registerApplication(appDesc.name());
+        DefaultApplication app =
+                new DefaultApplication(appId, appDesc.version(), appDesc.description(),
+                                       appDesc.origin(), appDesc.permissions(),
+                                       appDesc.featuresRepo(), appDesc.features());
+        apps.put(appId, app);
+        states.put(appId, INSTALLED);
+        delegate.notify(new ApplicationEvent(APP_INSTALLED, app));
+        return app;
+    }
+
+    @Override
+    public void remove(ApplicationId appId) {
+        Application app = apps.remove(appId);
+        if (app != null) {
+            states.remove(appId);
+            delegate.notify(new ApplicationEvent(APP_UNINSTALLED, app));
+            purgeApplication(app.id().name());
+        }
+    }
+
+    @Override
+    public void activate(ApplicationId appId) {
+        Application app = apps.get(appId);
+        if (app != null) {
+            setActive(appId.name());
+            states.put(appId, ACTIVE);
+            delegate.notify(new ApplicationEvent(APP_ACTIVATED, app));
+        }
+    }
+
+    @Override
+    public void deactivate(ApplicationId appId) {
+        Application app = apps.get(appId);
+        if (app != null) {
+            clearActive(appId.name());
+            states.put(appId, INSTALLED);
+            delegate.notify(new ApplicationEvent(APP_DEACTIVATED, app));
+        }
+    }
+
+    @Override
+    public Set<Permission> getPermissions(ApplicationId appId) {
+        return permissions.get(appId);
+    }
+
+    @Override
+    public void setPermissions(ApplicationId appId, Set<Permission> permissions) {
+        Application app = getApplication(appId);
+        if (app != null) {
+            this.permissions.put(appId, permissions);
+            delegate.notify(new ApplicationEvent(APP_PERMISSIONS_CHANGED, app));
+        }
+    }
+}
diff --git a/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleApplicationStoreTest.java b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleApplicationStoreTest.java
new file mode 100644
index 0000000..29006c4
--- /dev/null
+++ b/core/store/trivial/src/test/java/org/onosproject/store/trivial/impl/SimpleApplicationStoreTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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 org.onosproject.store.trivial.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.app.ApplicationEvent;
+import org.onosproject.app.ApplicationStoreDelegate;
+import org.onosproject.common.app.ApplicationArchive;
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.ApplicationIdStoreAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.core.Permission;
+
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.app.ApplicationEvent.Type.*;
+import static org.onosproject.app.ApplicationState.ACTIVE;
+import static org.onosproject.app.ApplicationState.INSTALLED;
+
+/**
+ * Test of the trivial application store implementation.
+ */
+public class SimpleApplicationStoreTest {
+
+    private SimpleApplicationStore store = new SimpleApplicationStore();
+    private TestDelegate delegate = new TestDelegate();
+
+    @Before
+    public void setUp() {
+        store.idStore = new TestIdStore();
+        store.setDelegate(delegate);
+        store.activate();
+    }
+
+    @After
+    public void tearDown() {
+        store.deactivate();
+    }
+
+    private Application createTestApp() {
+        return store.create(ApplicationArchive.class.getResourceAsStream("app.zip"));
+    }
+
+    @Test
+    public void create() {
+        Application app = createTestApp();
+        assertEquals("incorrect name", "org.foo.app", app.id().name());
+        assertEquals("incorrect app count", 1, store.getApplications().size());
+        assertEquals("incorrect app", app, store.getApplication(app.id()));
+        assertEquals("incorrect app state", INSTALLED, store.getState(app.id()));
+        assertEquals("incorrect event type", APP_INSTALLED, delegate.event.type());
+        assertEquals("incorrect event app", app, delegate.event.subject());
+    }
+
+    @Test
+    public void remove() {
+        Application app = createTestApp();
+        store.remove(app.id());
+        assertEquals("incorrect app count", 0, store.getApplications().size());
+        assertEquals("incorrect event type", APP_UNINSTALLED, delegate.event.type());
+        assertEquals("incorrect event app", app, delegate.event.subject());
+    }
+
+    @Test
+    public void activate() {
+        Application app = createTestApp();
+        store.activate(app.id());
+        assertEquals("incorrect app count", 1, store.getApplications().size());
+        assertEquals("incorrect app state", ACTIVE, store.getState(app.id()));
+        assertEquals("incorrect event type", APP_ACTIVATED, delegate.event.type());
+        assertEquals("incorrect event app", app, delegate.event.subject());
+    }
+
+    @Test
+    public void deactivate() {
+        Application app = createTestApp();
+        store.deactivate(app.id());
+        assertEquals("incorrect app count", 1, store.getApplications().size());
+        assertEquals("incorrect app state", INSTALLED, store.getState(app.id()));
+        assertEquals("incorrect event type", APP_DEACTIVATED, delegate.event.type());
+        assertEquals("incorrect event app", app, delegate.event.subject());
+    }
+
+    @Test
+    public void permissions() {
+        Application app = createTestApp();
+        ImmutableSet<Permission> permissions = ImmutableSet.of(new Permission() {
+        });
+        store.setPermissions(app.id(), permissions);
+        assertEquals("incorrect app perms", 1, store.getPermissions(app.id()).size());
+        assertEquals("incorrect app state", INSTALLED, store.getState(app.id()));
+        assertEquals("incorrect event type", APP_PERMISSIONS_CHANGED, delegate.event.type());
+        assertEquals("incorrect event app", app, delegate.event.subject());
+    }
+
+    private class TestIdStore extends ApplicationIdStoreAdapter {
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return new DefaultApplicationId(1, name);
+        }
+
+        @Override
+        public ApplicationId getAppId(String name) {
+            return new DefaultApplicationId(1, name);
+        }
+    }
+
+    private class TestDelegate implements ApplicationStoreDelegate {
+        private ApplicationEvent event;
+
+        @Override
+        public void notify(ApplicationEvent event) {
+            this.event = event;
+        }
+    }
+}
\ No newline at end of file