Adding support for user interface extensions.
Change-Id: I1e41d16efc11be31ad4c2fb0c09e86e3dfd26706
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/AbstractInjectionResource.java b/web/gui/src/main/java/org/onosproject/ui/impl/AbstractInjectionResource.java
new file mode 100644
index 0000000..2ed4ecd
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/AbstractInjectionResource.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.ui.impl;
+
+import org.onlab.rest.BaseResource;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Resource for serving semi-static resources.
+ */
+public class AbstractInjectionResource extends BaseResource {
+
+ /**
+ * Returns the index into the supplied string where the end of the
+ * specified pattern is located.
+ *
+ * @param string string to split
+ * @param start index where to start looking for pattern
+ * @param stopPattern optional pattern where to stop
+ */
+ protected int split(String string, int start, String stopPattern) {
+ int i = stopPattern != null ? string.indexOf(stopPattern, start) : string.length();
+ checkArgument(i > 0, "Unable to locate stop pattern %s", stopPattern);
+ return i + (stopPattern != null ? stopPattern.length() : 0);
+ }
+
+ /**
+ * Produces an input stream from the bytes of the specified sub-string.
+ *
+ * @param string source string
+ * @param start index where to start stream
+ * @param end index where to end stream
+ */
+ protected InputStream stream(String string, int start, int end) {
+ return new ByteArrayInputStream(string.substring(start, end).getBytes());
+ }
+
+ /**
+ * Auxiliary enumeration to sequence input streams.
+ */
+ protected class StreamEnumeration implements Enumeration<InputStream> {
+ private final Iterator<InputStream> iterator;
+
+ StreamEnumeration(List<InputStream> streams) {
+ this.iterator = streams.iterator();
+ }
+
+ @Override
+ public boolean hasMoreElements() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public InputStream nextElement() {
+ return iterator.next();
+ }
+ }
+}
diff --git a/web/gui/src/main/java/org/onosproject/gui/AbstractTableRow.java b/web/gui/src/main/java/org/onosproject/ui/impl/AbstractTableRow.java
similarity index 97%
rename from web/gui/src/main/java/org/onosproject/gui/AbstractTableRow.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/AbstractTableRow.java
index 7be7172..66db400 100644
--- a/web/gui/src/main/java/org/onosproject/gui/AbstractTableRow.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/AbstractTableRow.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
diff --git a/web/gui/src/main/java/org/onosproject/gui/DeviceGuiResource.java b/web/gui/src/main/java/org/onosproject/ui/impl/DeviceGuiResource.java
similarity index 98%
rename from web/gui/src/main/java/org/onosproject/gui/DeviceGuiResource.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/DeviceGuiResource.java
index 403b8c6..d409ebf 100644
--- a/web/gui/src/main/java/org/onosproject/gui/DeviceGuiResource.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/DeviceGuiResource.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
diff --git a/web/gui/src/main/java/org/onosproject/gui/DeviceTableRow.java b/web/gui/src/main/java/org/onosproject/ui/impl/DeviceTableRow.java
similarity index 98%
rename from web/gui/src/main/java/org/onosproject/gui/DeviceTableRow.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/DeviceTableRow.java
index 423657a..7e67e67 100644
--- a/web/gui/src/main/java/org/onosproject/gui/DeviceTableRow.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/DeviceTableRow.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import org.onosproject.net.Device;
import org.onosproject.net.device.DeviceService;
diff --git a/web/gui/src/main/java/org/onosproject/gui/GuiWebSocketServlet.java b/web/gui/src/main/java/org/onosproject/ui/impl/GuiWebSocketServlet.java
similarity index 98%
rename from web/gui/src/main/java/org/onosproject/gui/GuiWebSocketServlet.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/GuiWebSocketServlet.java
index 8d0eabe..5a660e0 100644
--- a/web/gui/src/main/java/org/onosproject/gui/GuiWebSocketServlet.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/GuiWebSocketServlet.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/MainExtResource.java b/web/gui/src/main/java/org/onosproject/ui/impl/MainExtResource.java
new file mode 100644
index 0000000..a1eae41
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/MainExtResource.java
@@ -0,0 +1,109 @@
+/*
+ * 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.ui.impl;
+
+import org.onosproject.ui.UiExtension;
+import org.onosproject.ui.UiExtensionService;
+import org.onosproject.ui.UiView;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.SequenceInputStream;
+
+import static com.google.common.collect.ImmutableList.of;
+import static com.google.common.io.ByteStreams.toByteArray;
+
+/**
+ * Resource for serving the dynamically composed onos.js.
+ */
+@Path("/")
+public class MainExtResource extends AbstractInjectionResource {
+
+ private static final String MAIN_JS = "/onos-template.js";
+ private static final String NAV_HTML = "/nav-template.html";
+
+ private static final String INJECT_VIEW_IDS = "// {INJECTED-VIEW-IDS}";
+ private static final String INJECT_VIEW_ITEMS = "<!-- {INJECTED-VIEW-NAV} -->";
+
+ private static final String NAV_FORMAT =
+ " <li> <a ng-click=\"navCtrl.hideNav()\" href=\"#/%s\">%s</a></li>";
+
+ @Path("/onos.js")
+ @GET
+ @Produces(MediaType.TEXT_HTML)
+ public Response getMainModule() throws IOException {
+ UiExtensionService service = get(UiExtensionService.class);
+ InputStream jsTemplate = getClass().getClassLoader().getResourceAsStream(MAIN_JS);
+ String js = new String(toByteArray(jsTemplate));
+
+ int p1 = split(js, 0, INJECT_VIEW_IDS);
+ int p2 = split(js, p1, null);
+
+ StreamEnumeration streams =
+ new StreamEnumeration(of(stream(js, 0, p1),
+ includeViewIds(service),
+ stream(js, p1, p2)));
+
+ return Response.ok(new SequenceInputStream(streams)).build();
+ }
+
+ // Produces an input stream including view id injections from all extensions.
+ private InputStream includeViewIds(UiExtensionService service) {
+ StringBuilder sb = new StringBuilder("\n");
+ for (UiExtension extension : service.getExtensions()) {
+ for (UiView view : extension.views()) {
+ sb.append(" '").append(view.id()).append("',");
+ }
+ }
+ return new ByteArrayInputStream(sb.toString().getBytes());
+ }
+
+ @Path("/nav/nav.html")
+ @GET
+ @Produces(MediaType.TEXT_HTML)
+ public Response getNavigation() throws IOException {
+ UiExtensionService service = get(UiExtensionService.class);
+ InputStream navTemplate = getClass().getClassLoader().getResourceAsStream(NAV_HTML);
+ String js = new String(toByteArray(navTemplate));
+
+ int p1 = split(js, 0, INJECT_VIEW_ITEMS);
+ int p2 = split(js, p1, null);
+
+ StreamEnumeration streams =
+ new StreamEnumeration(of(stream(js, 0, p1),
+ includeNavItems(service),
+ stream(js, p1, p2)));
+
+ return Response.ok(new SequenceInputStream(streams)).build();
+ }
+
+ // Produces an input stream including nav item injections from all extensions.
+ private InputStream includeNavItems(UiExtensionService service) {
+ StringBuilder sb = new StringBuilder("\n");
+ for (UiExtension extension : service.getExtensions()) {
+ for (UiView view : extension.views()) {
+ sb.append(String.format(NAV_FORMAT, view.id(), view.label()));
+ }
+ }
+ return new ByteArrayInputStream(sb.toString().getBytes());
+ }
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/MainIndexResource.java b/web/gui/src/main/java/org/onosproject/ui/impl/MainIndexResource.java
new file mode 100644
index 0000000..da4079f
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/MainIndexResource.java
@@ -0,0 +1,85 @@
+/*
+ * 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.ui.impl;
+
+import com.google.common.collect.ImmutableList;
+import org.onosproject.ui.UiExtension;
+import org.onosproject.ui.UiExtensionService;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.SequenceInputStream;
+
+import static com.google.common.collect.ImmutableList.of;
+import static com.google.common.io.ByteStreams.toByteArray;
+
+/**
+ * Resource for serving the dynamically composed index.html.
+ */
+@Path("/")
+public class MainIndexResource extends AbstractInjectionResource {
+
+ private static final String INDEX = "/index-template.html";
+
+ private static final String INJECT_CSS = "<!-- {INJECTED-STYLESHEETS} -->";
+ private static final String INJECT_JS = "<!-- {INJECTED-JAVASCRIPT} -->";
+
+ @Path("/")
+ @GET
+ @Produces(MediaType.TEXT_HTML)
+ public Response getMainIndex() throws IOException {
+ UiExtensionService service = get(UiExtensionService.class);
+ InputStream indexTemplate = getClass().getClassLoader().getResourceAsStream(INDEX);
+ String index = new String(toByteArray(indexTemplate));
+
+ int p1 = split(index, 0, INJECT_JS);
+ int p2 = split(index, p1, INJECT_CSS);
+ int p3 = split(index, p2, null);
+
+ StreamEnumeration streams =
+ new StreamEnumeration(of(stream(index, 0, p1),
+ includeJs(service),
+ stream(index, p1, p2),
+ includeCss(service),
+ stream(index, p2, p3)));
+
+ return Response.ok(new SequenceInputStream(streams)).build();
+ }
+
+ // Produces an input stream including CSS injections from all extensions.
+ private InputStream includeCss(UiExtensionService service) {
+ ImmutableList.Builder<InputStream> builder = ImmutableList.builder();
+ for (UiExtension extension : service.getExtensions()) {
+ builder.add(extension.css());
+ }
+ return new SequenceInputStream(new StreamEnumeration(builder.build()));
+ }
+
+ // Produces an input stream including JS injections from all extensions.
+ private InputStream includeJs(UiExtensionService service) {
+ ImmutableList.Builder<InputStream> builder = ImmutableList.builder();
+ for (UiExtension extension : service.getExtensions()) {
+ builder.add(extension.js());
+ }
+ return new SequenceInputStream(new StreamEnumeration(builder.build()));
+ }
+
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/MainViewResource.java b/web/gui/src/main/java/org/onosproject/ui/impl/MainViewResource.java
new file mode 100644
index 0000000..26b5d8c
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/MainViewResource.java
@@ -0,0 +1,59 @@
+/*
+ * 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.ui.impl;
+
+import org.onosproject.ui.UiExtension;
+import org.onosproject.ui.UiExtensionService;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM;
+import static javax.ws.rs.core.MediaType.TEXT_HTML;
+
+/**
+ * Resource for serving the dynamically composed onos.js.
+ */
+@Path("/")
+public class MainViewResource extends AbstractInjectionResource {
+
+ private static final String CONTENT_TYPE = "Content-Type";
+ private static final String STYLESHEET = "text/css";
+ private static final String SCRIPT = "text/javascript";
+
+ @Path("{view}/{resource}")
+ @GET
+ public Response getViewResource(@PathParam("view") String viewId,
+ @PathParam("resource") String resource) throws IOException {
+ UiExtensionService service = get(UiExtensionService.class);
+ UiExtension extension = service.getViewExtension(viewId);
+ return extension != null ?
+ Response.ok(extension.resource(viewId, resource))
+ .header(CONTENT_TYPE, contentType(resource)).build() :
+ Response.status(Response.Status.NOT_FOUND).build();
+ }
+
+ static String contentType(String resource) {
+ return resource.endsWith(".html") ? TEXT_HTML :
+ resource.endsWith(".css") ? STYLESHEET :
+ resource.endsWith(".js") ? SCRIPT :
+ APPLICATION_OCTET_STREAM;
+ }
+
+}
diff --git a/web/gui/src/main/java/org/onosproject/gui/RowComparator.java b/web/gui/src/main/java/org/onosproject/ui/impl/RowComparator.java
similarity index 98%
rename from web/gui/src/main/java/org/onosproject/gui/RowComparator.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/RowComparator.java
index 7c79fc2..b588f48 100644
--- a/web/gui/src/main/java/org/onosproject/gui/RowComparator.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/RowComparator.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import java.util.Comparator;
diff --git a/web/gui/src/main/java/org/onosproject/gui/TableRow.java b/web/gui/src/main/java/org/onosproject/ui/impl/TableRow.java
similarity index 96%
rename from web/gui/src/main/java/org/onosproject/gui/TableRow.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/TableRow.java
index 67c8449..81a14ba 100644
--- a/web/gui/src/main/java/org/onosproject/gui/TableRow.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TableRow.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
diff --git a/web/gui/src/main/java/org/onosproject/gui/TopologyResource.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyResource.java
similarity index 98%
rename from web/gui/src/main/java/org/onosproject/gui/TopologyResource.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/TopologyResource.java
index 980be31..95f9a07 100644
--- a/web/gui/src/main/java/org/onosproject/gui/TopologyResource.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyResource.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
diff --git a/web/gui/src/main/java/org/onosproject/gui/TopologyViewIntentFilter.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewIntentFilter.java
similarity index 99%
rename from web/gui/src/main/java/org/onosproject/gui/TopologyViewIntentFilter.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewIntentFilter.java
index 0549604..f8b1d6c 100644
--- a/web/gui/src/main/java/org/onosproject/gui/TopologyViewIntentFilter.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewIntentFilter.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
diff --git a/web/gui/src/main/java/org/onosproject/gui/TopologyViewMessages.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessages.java
similarity index 99%
rename from web/gui/src/main/java/org/onosproject/gui/TopologyViewMessages.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessages.java
index 746c37e..dd891cd 100644
--- a/web/gui/src/main/java/org/onosproject/gui/TopologyViewMessages.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessages.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
diff --git a/web/gui/src/main/java/org/onosproject/gui/TopologyViewWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewWebSocket.java
similarity index 99%
rename from web/gui/src/main/java/org/onosproject/gui/TopologyViewWebSocket.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewWebSocket.java
index 44ad026..e259cb5 100644
--- a/web/gui/src/main/java/org/onosproject/gui/TopologyViewWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewWebSocket.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
new file mode 100644
index 0000000..6ca1e21
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
@@ -0,0 +1,96 @@
+/*
+ * 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.ui.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+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.Service;
+import org.onosproject.ui.UiExtension;
+import org.onosproject.ui.UiExtensionService;
+import org.onosproject.ui.UiView;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.collect.ImmutableList.of;
+import static java.util.stream.Collectors.toSet;
+
+/**
+ * Manages the user interface extensions.
+ */
+@Component(immediate = true)
+@Service
+public class UiExtensionManager implements UiExtensionService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ // List of all extensions
+ private final List<UiExtension> extensions = Lists.newArrayList();
+
+ // Map of views to extensions
+ private final Map<String, UiExtension> views = Maps.newHashMap();
+
+ // Core views & core extension
+ private final List<UiView> coreViews = of(new UiView("sample", "Sample"),
+ new UiView("topo", "Topology View"),
+ new UiView("device", "Devices"));
+
+ private final UiExtension core = new UiExtension(coreViews, getClass().getClassLoader());
+
+ @Activate
+ public void activate() {
+ register(core);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ unregister(core);
+ log.info("Stopped");
+ }
+
+ @Override
+ public synchronized void register(UiExtension extension) {
+ if (!extensions.contains(extension)) {
+ extensions.add(extension);
+ for (UiView view : extension.views()) {
+ views.put(view.id(), extension);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void unregister(UiExtension extension) {
+ extensions.remove(extension);
+ extension.views().stream().map(UiView::id).collect(toSet()).forEach(views::remove);
+ }
+
+ @Override
+ public synchronized List<UiExtension> getExtensions() {
+ return ImmutableList.copyOf(extensions);
+ }
+
+ @Override
+ public synchronized UiExtension getViewExtension(String viewId) {
+ return views.get(viewId);
+ }
+}
diff --git a/web/gui/src/main/java/org/onosproject/gui/package-info.java b/web/gui/src/main/java/org/onosproject/ui/impl/package-info.java
similarity index 95%
rename from web/gui/src/main/java/org/onosproject/gui/package-info.java
rename to web/gui/src/main/java/org/onosproject/ui/impl/package-info.java
index 7a91308..cb9ae2f 100644
--- a/web/gui/src/main/java/org/onosproject/gui/package-info.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/package-info.java
@@ -17,4 +17,4 @@
/**
* Set of resources providing data for the ONOS GUI.
*/
-package org.onosproject.gui;
+package org.onosproject.ui.impl;