Repository: ambari Updated Branches: refs/heads/trunk ccdd4af03 -> 6d14390a3
AMBARI-5914 - Views: View server-side events Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/6d14390a Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/6d14390a Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/6d14390a Branch: refs/heads/trunk Commit: 6d14390a3691f249b8465e274605345877b0902a Parents: ccdd4af Author: tbeerbower <[email protected]> Authored: Wed May 28 15:37:13 2014 -0400 Committer: tbeerbower <[email protected]> Committed: Wed May 28 16:22:06 2014 -0400 ---------------------------------------------------------------------- .../ambari/server/orm/entities/ViewEntity.java | 64 ++++++--- .../server/orm/entities/ViewInstanceEntity.java | 73 +++++----- .../ambari/server/view/ViewContextImpl.java | 121 +++++++++++++++-- .../apache/ambari/server/view/ViewRegistry.java | 94 ++++++++++++- .../server/view/configuration/ViewConfig.java | 41 +++++- .../ambari/server/view/events/EventImpl.java | 132 +++++++++++++++++++ .../ambari/server/view/ViewRegistryTest.java | 55 ++++++++ .../view/configuration/ViewConfigTest.java | 12 ++ .../server/view/events/EventImplTest.java | 89 +++++++++++++ .../main/java/org/apache/ambari/view/View.java | 46 +++++++ .../org/apache/ambari/view/ViewContext.java | 52 +++++++- .../org/apache/ambari/view/ViewController.java | 56 ++++++++ .../org/apache/ambari/view/ViewDefinition.java | 46 +++++++ .../ambari/view/ViewInstanceDefinition.java | 62 +++++++++ .../org/apache/ambari/view/events/Event.java | 57 ++++++++ .../org/apache/ambari/view/events/Listener.java | 32 +++++ 16 files changed, 956 insertions(+), 76 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java index 84724c8..25318b5 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewEntity.java @@ -24,6 +24,8 @@ import org.apache.ambari.server.controller.spi.ResourceProvider; import org.apache.ambari.server.view.ViewSubResourceDefinition; import org.apache.ambari.server.view.configuration.ResourceConfig; import org.apache.ambari.server.view.configuration.ViewConfig; +import org.apache.ambari.view.View; +import org.apache.ambari.view.ViewDefinition; import javax.persistence.Basic; import javax.persistence.CascadeType; @@ -47,7 +49,7 @@ import java.util.Set; @NamedQuery(name = "allViews", query = "SELECT view FROM ViewEntity view") @Entity -public class ViewEntity { +public class ViewEntity implements ViewDefinition { /** * The unique view name. */ @@ -146,6 +148,12 @@ public class ViewEntity { @Transient private String commonName = null; + /** + * The view. + */ + @Transient + private View view = null; + // ----- Constructors ------------------------------------------------------ @@ -186,6 +194,24 @@ public class ViewEntity { } + // ----- ViewDefinition ---------------------------------------------------- + + @Override + public String getViewName() { + return getCommonName(); + } + + @Override + public String getLabel() { + return label; + } + + @Override + public String getVersion() { + return version; + } + + // ----- ViewEntity -------------------------------------------------------- /** @@ -221,15 +247,6 @@ public class ViewEntity { } /** - * Get the view label (display name). - * - * @return the view label - */ - public String getLabel() { - return label; - } - - /** * Set the view label (display name). * * @param label the view label @@ -239,15 +256,6 @@ public class ViewEntity { } /** - * Get the view version. - * - * @return the version - */ - public String getVersion() { - return version; - } - - /** * Set the view version. * * @param version the version @@ -502,6 +510,24 @@ public class ViewEntity { return configuration; } + /** + * Set the view. + * + * @param view the view + */ + public void setView(View view) { + this.view = view; + } + + /** + * Get the associated view. + * + * @return the view + */ + public View getView() { + return view; + } + // ----- helper methods ---------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java index 7b29098..70a60b9 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java @@ -21,6 +21,9 @@ package org.apache.ambari.server.orm.entities; import org.apache.ambari.server.controller.spi.Resource; import org.apache.ambari.server.view.configuration.InstanceConfig; import org.apache.ambari.view.ResourceProvider; +import org.apache.ambari.view.ViewDefinition; +import org.apache.ambari.view.ViewInstanceDefinition; +import org.apache.ambari.view.events.Listener; import javax.persistence.CascadeType; import javax.persistence.Column; @@ -45,7 +48,7 @@ import java.util.Map; @NamedQuery(name = "allViewInstances", query = "SELECT viewInstance FROM ViewInstanceEntity viewInstance") @Entity -public class ViewInstanceEntity { +public class ViewInstanceEntity implements ViewInstanceDefinition { /** * The prefix for every view instance context path. */ @@ -142,17 +145,45 @@ public class ViewInstanceEntity { } - // ----- ViewInstanceEntity ------------------------------------------------ + // ----- ViewInstanceDefinition -------------------------------------------- - /** - * Get the view name. - * - * @return the view name - */ + @Override + public String getInstanceName() { + return name; + } + + @Override public String getViewName() { return viewName; } + @Override + public Map<String, String> getPropertyMap() { + Map<String, String> propertyMap = new HashMap<String, String>(); + + for (ViewInstancePropertyEntity viewInstancePropertyEntity : properties) { + propertyMap.put(viewInstancePropertyEntity.getName(), viewInstancePropertyEntity.getValue()); + } + return propertyMap; + } + + @Override + public Map<String, String> getInstanceDataMap() { + Map<String, String> applicationData = new HashMap<String, String>(); + + for (ViewInstanceDataEntity viewInstanceDataEntity : data) { + applicationData.put(viewInstanceDataEntity.getName(), viewInstanceDataEntity.getValue()); + } + return applicationData; + } + + @Override + public ViewDefinition getViewDefinition() { + return view; + } + +// ----- ViewInstanceEntity ------------------------------------------------ + /** * Set the view name. * @@ -190,20 +221,6 @@ public class ViewInstanceEntity { } /** - * Get the instance property map. - * - * @return the map of instance properties - */ - public Map<String, String> getPropertyMap() { - Map<String, String> propertyMap = new HashMap<String, String>(); - - for (ViewInstancePropertyEntity viewInstancePropertyEntity : properties) { - propertyMap.put(viewInstancePropertyEntity.getName(), viewInstancePropertyEntity.getValue()); - } - return propertyMap; - } - - /** * Add a property value to this instance. * * @param key the property key @@ -294,20 +311,6 @@ public class ViewInstanceEntity { } /** - * Get the view instance application data. - * - * @return the view instance application data map - */ - public Map<String, String> getInstanceDataMap() { - Map<String, String> applicationData = new HashMap<String, String>(); - - for (ViewInstanceDataEntity viewInstanceDataEntity : data) { - applicationData.put(viewInstanceDataEntity.getName(), viewInstanceDataEntity.getValue()); - } - return applicationData; - } - - /** * Associate the given instance data value with the given key. * * @param key the key http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java index 1f936e7..f952d61 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java @@ -21,13 +21,20 @@ package org.apache.ambari.server.view; import com.google.inject.Guice; import com.google.inject.Injector; import org.apache.ambari.server.configuration.ComponentSSLConfiguration; +import org.apache.ambari.server.orm.entities.ViewEntity; import org.apache.ambari.server.orm.entities.ViewInstanceEntity; +import org.apache.ambari.server.view.events.EventImpl; import org.apache.ambari.server.view.persistence.DataStoreImpl; import org.apache.ambari.server.view.persistence.DataStoreModule; import org.apache.ambari.view.DataStore; import org.apache.ambari.view.ResourceProvider; import org.apache.ambari.view.URLStreamProvider; import org.apache.ambari.view.ViewContext; +import org.apache.ambari.view.ViewController; +import org.apache.ambari.view.ViewDefinition; +import org.apache.ambari.view.ViewInstanceDefinition; +import org.apache.ambari.view.events.Event; +import org.apache.ambari.view.events.Listener; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -35,15 +42,22 @@ import org.springframework.security.core.userdetails.UserDetails; import java.io.IOException; import java.io.InputStream; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; /** * View context implementation. */ -public class ViewContextImpl implements ViewContext { +public class ViewContextImpl implements ViewContext, ViewController { + + /** + * The associated view definition. + */ + private final ViewEntity viewEntity; /** * The associated view definition. @@ -69,64 +83,94 @@ public class ViewContextImpl implements ViewContext { // ---- Constructors ------------------------------------------------------- /** - * Construct a view context from the given view entity. + * Construct a view context from the given view instance entity. * * @param viewInstanceEntity the view entity * @param viewRegistry the view registry */ public ViewContextImpl(ViewInstanceEntity viewInstanceEntity, ViewRegistry viewRegistry) { + this.viewEntity = viewInstanceEntity.getViewEntity(); this.viewInstanceEntity = viewInstanceEntity; this.viewRegistry = viewRegistry; this.streamProvider = ViewURLStreamProvider.getProvider(); } + /** + * Construct a view context from the given view entity. + * + * @param viewEntity the view entity + * @param viewRegistry the view registry + */ + public ViewContextImpl(ViewEntity viewEntity, ViewRegistry viewRegistry) { + this.viewEntity = viewEntity; + this.viewInstanceEntity = null; + this.viewRegistry = viewRegistry; + this.streamProvider = ViewURLStreamProvider.getProvider(); + } // ----- ViewContext ------------------------------------------------------- @Override public String getViewName() { - return viewInstanceEntity.getViewEntity().getCommonName(); + return viewEntity.getCommonName(); + } + + @Override + public ViewDefinition getViewDefinition() { + return viewEntity; } @Override public String getInstanceName() { - return viewInstanceEntity.getName(); + return viewInstanceEntity == null ? null : viewInstanceEntity.getName(); + } + + @Override + public ViewInstanceDefinition getViewInstanceDefinition() { + return viewInstanceEntity; } @Override public Map<String, String> getProperties() { - return Collections.unmodifiableMap(viewInstanceEntity.getPropertyMap()); + return viewInstanceEntity == null ? null : + Collections.unmodifiableMap(viewInstanceEntity.getPropertyMap()); } @Override public void putInstanceData(String key, String value) { + checkInstance(); viewInstanceEntity.putInstanceData(key, value); viewRegistry.updateViewInstance(viewInstanceEntity); } @Override public String getInstanceData(String key) { - return viewInstanceEntity.getInstanceDataMap().get(key); + return viewInstanceEntity == null ? null : + viewInstanceEntity.getInstanceDataMap().get(key); } @Override public Map<String, String> getInstanceData() { - return Collections.unmodifiableMap(viewInstanceEntity.getInstanceDataMap()); + return viewInstanceEntity == null ? null : + Collections.unmodifiableMap(viewInstanceEntity.getInstanceDataMap()); } @Override public void removeInstanceData(String key) { + checkInstance(); viewRegistry.removeInstanceData(viewInstanceEntity, key); } @Override public String getAmbariProperty(String key) { - return viewInstanceEntity.getViewEntity().getAmbariProperty(key); + return viewInstanceEntity == null ? null : + viewInstanceEntity.getViewEntity().getAmbariProperty(key); } @Override public ResourceProvider<?> getResourceProvider(String type) { - return viewInstanceEntity.getResourceProvider(type); + return viewInstanceEntity == null ? null : + viewInstanceEntity.getResourceProvider(type); } @Override @@ -151,13 +195,66 @@ public class ViewContextImpl implements ViewContext { @Override public synchronized DataStore getDataStore() { - if (dataStore == null) { - Injector injector = Guice.createInjector(new DataStoreModule(viewInstanceEntity)); - dataStore = injector.getInstance(DataStoreImpl.class); + if (viewInstanceEntity != null) { + if (dataStore == null) { + Injector injector = Guice.createInjector(new DataStoreModule(viewInstanceEntity)); + dataStore = injector.getInstance(DataStoreImpl.class); + } } return dataStore; } + @Override + public Collection<ViewDefinition> getViewDefinitions() { + return Collections.<ViewDefinition>unmodifiableCollection(viewRegistry.getDefinitions()); + } + + @Override + public Collection<ViewInstanceDefinition> getViewInstanceDefinitions() { + Collection<ViewInstanceEntity> instanceDefinitions = new HashSet<ViewInstanceEntity>(); + for (ViewEntity viewEntity : viewRegistry.getDefinitions()) { + instanceDefinitions.addAll(viewRegistry.getInstanceDefinitions(viewEntity)); + } + return Collections.<ViewInstanceDefinition>unmodifiableCollection(instanceDefinitions); + } + + @Override + public ViewController getController() { + return this; + } + + + // ----- ViewController ---------------------------------------------------- + + @Override + public void fireEvent(String eventId, Map<String, String> eventProperties) { + Event event = viewInstanceEntity == null ? + new EventImpl(eventId, eventProperties, viewEntity) : + new EventImpl(eventId, eventProperties, viewInstanceEntity); + + viewRegistry.fireEvent(event); + } + + @Override + public void registerListener(Listener listener, String viewName) { + viewRegistry.registerListener(listener, viewName, null); + } + + @Override + public void registerListener(Listener listener, String viewName, String viewVersion) { + viewRegistry.registerListener(listener, viewName, viewVersion); + } + + + // ----- helper methods ---------------------------------------------------- + + // check for an associated instance + private void checkInstance() { + if (viewInstanceEntity == null) { + throw new IllegalStateException("No instance is associated with the context."); + } + } + // ----- Inner class : ViewURLStreamProvider ------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java index c81bb5f..466ce5a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java @@ -44,8 +44,12 @@ import org.apache.ambari.server.view.configuration.PropertyConfig; import org.apache.ambari.server.view.configuration.ResourceConfig; import org.apache.ambari.server.view.configuration.ViewConfig; import org.apache.ambari.view.SystemException; +import org.apache.ambari.view.View; import org.apache.ambari.view.ViewContext; +import org.apache.ambari.view.ViewDefinition; import org.apache.ambari.view.ViewResourceHandler; +import org.apache.ambari.view.events.Event; +import org.apache.ambari.view.events.Listener; import org.eclipse.jetty.webapp.WebAppContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -105,6 +109,12 @@ public class ViewRegistry { new HashMap<String, Set<SubResourceDefinition>>(); /** + * Mapping of view names to registered listeners. + */ + private final Map<String, List<Listener>> listeners = + new HashMap<String, List<Listener>>(); + + /** * Helper class. */ private ViewRegistryHelper helper = new ViewRegistryHelper(); @@ -168,6 +178,10 @@ public class ViewRegistry { * @param definition the definition */ public void addDefinition(ViewEntity definition) { + View view = definition.getView(); + if (view != null) { + view.onDeploy(definition); + } viewDefinitions.put(definition.getName(), definition); } @@ -216,6 +230,11 @@ public class ViewRegistry { instanceDefinitions = new HashMap<String, ViewInstanceEntity>(); viewInstanceDefinitions.put(definition, instanceDefinitions); } + + View view = definition.getView(); + if (view != null) { + view.onCreate(instanceDefinition); + } instanceDefinitions.put(instanceDefinition.getName(), instanceDefinition); } @@ -228,7 +247,15 @@ public class ViewRegistry { public void removeInstanceDefinition(ViewEntity definition, String instanceName) { Map<String, ViewInstanceEntity> instanceDefinitions = viewInstanceDefinitions.get(definition); if (instanceDefinitions != null) { - instanceDefinitions.remove(instanceName); + + ViewInstanceEntity instanceDefinition = instanceDefinitions.get(instanceName); + if (instanceDefinition != null) { + View view = definition.getView(); + if (view != null) { + view.onDestroy(instanceDefinition); + } + instanceDefinitions.remove(instanceName); + } } } @@ -469,6 +496,40 @@ public class ViewRegistry { } } + /** + * Notify any registered listeners of the given event. + * + * @param event the event + */ + public void fireEvent(Event event) { + + ViewDefinition subject = event.getViewSubject(); + + fireEvent(event, subject.getViewName()); + fireEvent(event, ViewEntity.getViewName(subject.getViewName(), subject.getVersion())); + } + + /** + * Register the given listener to listen for events from the view identified by the given name and version. + * + * @param listener the listener + * @param viewName the view name + * @param viewVersion the view version; null indicates all versions + */ + public synchronized void registerListener(Listener listener, String viewName, String viewVersion) { + + String name = viewVersion == null ? viewName : ViewEntity.getViewName(viewName, viewVersion); + + List<Listener> listeners = this.listeners.get(name); + + if (listeners == null) { + listeners = new LinkedList<Listener>(); + this.listeners.put(name, listeners); + } + + listeners.add(listener); + } + // ----- helper methods ---------------------------------------------------- @@ -479,6 +540,7 @@ public class ViewRegistry { viewDefinitions.clear(); viewInstanceDefinitions.clear(); subResourceDefinitionsMap.clear(); + listeners.clear(); } /** @@ -562,6 +624,12 @@ public class ViewRegistry { } viewDefinition.setResources(resources); } + View view = null; + if (viewConfig.getView() != null) { + view = getView(viewConfig.getViewClass(cl), new ViewContextImpl(viewDefinition, this)); + } + viewDefinition.setView(view); + return viewDefinition; } @@ -675,6 +743,19 @@ public class ViewRegistry { return viewInstanceInjector.getInstance(clazz); } + // get the given view class from the given class loader; inject a context + private static View getView(Class<? extends View> clazz, + final ViewContext viewContext) { + Injector viewInstanceInjector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(ViewContext.class) + .toInstance(viewContext); + } + }); + return viewInstanceInjector.getInstance(clazz); + } + // remove undeployed views from the ambari db private void removeUndeployedViews() { for (ViewEntity viewEntity : viewDAO.findAll()) { @@ -824,6 +905,17 @@ public class ViewRegistry { return URLClassLoader.newInstance(urlList.toArray(new URL[urlList.size()])); } + // notify the view identified by the given view name of the given event + private void fireEvent(Event event, String viewName) { + List<Listener> listeners = this.listeners.get(viewName); + + if (listeners != null) { + for (Listener listener : listeners) { + listener.notify(event); + } + } + } + /** * Static initialization of DAO. * http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java index 17d2619..a49c9dd 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java @@ -18,15 +18,14 @@ package org.apache.ambari.server.view.configuration; -import javax.servlet.http.HttpServlet; +import org.apache.ambari.view.View; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * View configuration. @@ -50,6 +49,17 @@ public class ViewConfig { private String version; /** + * The main view class name. + */ + @XmlElement(name="view-class") + private String view; + + /** + * The view class. + */ + private Class<? extends View> viewClass = null; + + /** * The list of view parameters. */ @XmlElement(name="parameter") @@ -101,6 +111,31 @@ public class ViewConfig { } /** + * Get the view class name. + * + * @return the view class name + */ + public String getView() { + return view; + } + + /** + * Get the view class. + * + * @param cl the class loader + * + * @return the view class + * + * @throws ClassNotFoundException if the class can not be loaded + */ + public Class<? extends View> getViewClass(ClassLoader cl) throws ClassNotFoundException { + if (viewClass == null) { + viewClass = cl.loadClass(view).asSubclass(View.class); + } + return viewClass; + } + + /** * Get the list of view parameters. * * @return the list of parameters http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-server/src/main/java/org/apache/ambari/server/view/events/EventImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/events/EventImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/view/events/EventImpl.java new file mode 100644 index 0000000..c4eab26 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/view/events/EventImpl.java @@ -0,0 +1,132 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.view.events; + +import org.apache.ambari.view.ViewDefinition; +import org.apache.ambari.view.ViewInstanceDefinition; +import org.apache.ambari.view.events.Event; + +import java.util.Collections; +import java.util.Map; + +/** + * View event implementation. + */ +public class EventImpl implements Event { + /** + * The event id. + */ + private final String id; + + /** + * The event properties. + */ + private final Map<String, String> properties; + + /** + * The view subject of the event. + */ + private final ViewDefinition viewSubject; + + /** + * The instance subject of the event. + */ + private final ViewInstanceDefinition viewInstanceSubject; + + + // ----- Constructors ------------------------------------------------------ + + /** + * Construct an event. + * + * @param id the event id + * @param properties the event properties + * @param viewSubject the subject of the event + */ + public EventImpl(String id, Map<String, String> properties, ViewDefinition viewSubject) { + this(id, properties, viewSubject, null); + } + + /** + * Construct an event. + * + * @param id the event id + * @param properties the event properties + * @param viewInstanceSubject the subject of the event + */ + public EventImpl(String id, Map<String, String> properties, ViewInstanceDefinition viewInstanceSubject) { + this(id, properties, viewInstanceSubject.getViewDefinition(), viewInstanceSubject); + } + + // private constructor + private EventImpl(String id, Map<String, String> properties, + ViewDefinition viewSubject, ViewInstanceDefinition viewInstanceSubject) { + this.id = id; + this.viewSubject = viewSubject; + this.viewInstanceSubject = viewInstanceSubject; + this.properties = properties == null ? Collections.<String, String>emptyMap() : + Collections.unmodifiableMap(properties); + } + + + // ----- Event ------------------------------------------------------------- + + /** + * Get the event identifier. + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * Get the event properties. + * + * @return the properties + */ + public Map<String, String> getProperties() { + return properties; + } + + /** + * Get the view subject of the event. + * + * @return the view subject + */ + public ViewDefinition getViewSubject() { + return viewSubject; + } + + /** + * Get the instance subject of the event. + * + * @return the instance subject; null if this is a view level event + */ + public ViewInstanceDefinition getViewInstanceSubject() { + return viewInstanceSubject; + } + + // ----- Object overrides -------------------------------------------------- + + @Override + public String toString() { + return "View Event " + id; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java index 5754680..05947d5 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java @@ -34,6 +34,10 @@ import org.apache.ambari.server.view.configuration.InstanceConfigTest; import org.apache.ambari.server.view.configuration.ResourceConfig; import org.apache.ambari.server.view.configuration.ResourceConfigTest; import org.apache.ambari.server.view.configuration.ViewConfig; +import org.apache.ambari.server.view.events.EventImpl; +import org.apache.ambari.server.view.events.EventImplTest; +import org.apache.ambari.view.events.Event; +import org.apache.ambari.view.events.Listener; import org.easymock.Capture; import org.junit.AfterClass; import org.junit.Assert; @@ -70,6 +74,18 @@ import static org.easymock.EasyMock.verify; */ public class ViewRegistryTest { + private static String view_xml1 = "<view>\n" + + " <name>MY_VIEW</name>\n" + + " <label>My View!</label>\n" + + " <version>1.0.0</version>\n" + + "</view>"; + + private static String view_xml2 = "<view>\n" + + " <name>MY_VIEW</name>\n" + + " <label>My View!</label>\n" + + " <version>2.0.0</version>\n" + + "</view>"; + @Test public void testReadViewArchives() throws Exception { Configuration configuration = createNiceMock(Configuration.class); @@ -289,6 +305,28 @@ public class ViewRegistryTest { } @Test + public void testListener() throws Exception { + ViewRegistry registry = ViewRegistry.getInstance(); + + TestListener listener = new TestListener(); + registry.registerListener(listener, "MY_VIEW", "1.0.0"); + + EventImpl event = EventImplTest.getEvent("MyEvent", Collections.<String, String>emptyMap(), view_xml1); + + registry.fireEvent(event); + + Assert.assertEquals(event, listener.getLastEvent()); + + listener.clear(); + + event = EventImplTest.getEvent("MyEvent", Collections.<String, String>emptyMap(), view_xml2); + + registry.fireEvent(event); + + Assert.assertNull(listener.getLastEvent()); + } + + @Test public void testAddGetDefinitions() throws Exception { ViewEntity viewDefinition = ViewEntityTest.getViewEntity(); @@ -436,4 +474,21 @@ public class ViewRegistryTest { } } + private static class TestListener implements Listener { + private Event lastEvent = null; + + @Override + public void notify(Event event) { + lastEvent = event; + } + + public Event getLastEvent() { + return lastEvent; + } + + public void clear() { + lastEvent = null; + } + } + } http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java index 42dd7be..c44e40b 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/configuration/ViewConfigTest.java @@ -100,6 +100,12 @@ public class ViewConfigTest { " <version>1.0.0</version>\n" + "</view>"; + private static String view_class_xml = "<view>\n" + + " <name>MY_VIEW</name>\n" + + " <label>My View!</label>\n" + + " <version>1.0.0</version>\n" + + " <view-class>ViewImpl</view-class>\n" + + "</view>"; @Test public void testGetName() throws Exception { @@ -120,6 +126,12 @@ public class ViewConfigTest { } @Test + public void testGetView() throws Exception { + ViewConfig config = getConfig(view_class_xml); + Assert.assertEquals("ViewImpl", config.getView()); + } + + @Test public void testGetParameters() throws Exception { ViewConfig config = getConfig(); List<ParameterConfig> parameters = config.getParameters(); http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-server/src/test/java/org/apache/ambari/server/view/events/EventImplTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/events/EventImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/events/EventImplTest.java new file mode 100644 index 0000000..6ad4610 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/view/events/EventImplTest.java @@ -0,0 +1,89 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.view.events; + +import org.apache.ambari.server.orm.entities.ViewEntity; +import org.apache.ambari.server.orm.entities.ViewEntityTest; +import org.apache.ambari.server.orm.entities.ViewInstanceEntity; +import org.apache.ambari.server.orm.entities.ViewInstanceEntityTest; +import org.apache.ambari.server.view.configuration.ViewConfig; +import org.apache.ambari.server.view.configuration.ViewConfigTest; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * EventImpl tests. + */ +public class EventImplTest { + + private static String view_xml = "<view>\n" + + " <name>MY_VIEW</name>\n" + + " <label>My View!</label>\n" + + " <version>1.0.0</version>\n" + + "</view>"; + + @Test + public void testGetId() throws Exception { + EventImpl event = getEvent("MyEvent", Collections.<String, String>emptyMap(), view_xml); + Assert.assertEquals("MyEvent", event.getId()); + } + + @Test + public void testGetProperties() throws Exception { + Map<String, String> properties = new HashMap<String, String>(); + properties.put("p1", "v1"); + properties.put("p2", "v2"); + + EventImpl event = getEvent("MyEvent", properties, view_xml); + Assert.assertEquals(properties, event.getProperties()); + } + + @Test + public void testGetViewSubject() throws Exception { + EventImpl event = getEvent("MyEvent", Collections.<String, String>emptyMap(), view_xml); + + Assert.assertEquals("MY_VIEW", event.getViewSubject().getViewName()); + Assert.assertEquals("My View!", event.getViewSubject().getLabel()); + Assert.assertEquals("1.0.0", event.getViewSubject().getVersion()); + } + + @Test + public void testGetViewInstanceSubject() throws Exception { + EventImpl event = getEvent("MyEvent", Collections.<String, String>emptyMap(), view_xml); + Assert.assertNull(event.getViewInstanceSubject()); + + ViewInstanceEntity viewInstanceEntity = ViewInstanceEntityTest.getViewInstanceEntity(); + event = getEvent("MyEvent", Collections.<String, String>emptyMap(), viewInstanceEntity); + Assert.assertEquals(viewInstanceEntity, event.getViewInstanceSubject()); + } + + public static EventImpl getEvent(String id, Map<String, String> properties, String xml) throws Exception{ + ViewConfig viewConfig = ViewConfigTest.getConfig(xml); + ViewEntity viewEntity = ViewEntityTest.getViewEntity(viewConfig); + return new EventImpl(id, properties, viewEntity); + } + + + public static EventImpl getEvent(String id, Map<String, String> properties, ViewInstanceEntity instanceEntity) throws Exception{ + return new EventImpl(id, properties, instanceEntity); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-views/src/main/java/org/apache/ambari/view/View.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/View.java b/ambari-views/src/main/java/org/apache/ambari/view/View.java new file mode 100644 index 0000000..4de1746 --- /dev/null +++ b/ambari-views/src/main/java/org/apache/ambari/view/View.java @@ -0,0 +1,46 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.view; + +/** + * Interface for main view class. + */ +public interface View { + + /** + * Called by the view framework when the given view is being deployed. + * + * @param definition the view definition + */ + public void onDeploy(ViewDefinition definition); + + /** + * Called by the view framework when the given instance is being created. + * + * @param definition the view instance definition + */ + public void onCreate(ViewInstanceDefinition definition); + + /** + * Called by the view framework when the given instance is being destroyed. + * + * @param definition the view instance definition + */ + public void onDestroy(ViewInstanceDefinition definition); +} http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java b/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java index 898e8f5..c83e8eb 100644 --- a/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java +++ b/ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java @@ -18,6 +18,7 @@ package org.apache.ambari.view; +import java.util.Collection; import java.util.Map; /** @@ -47,16 +48,30 @@ public interface ViewContext { public String getViewName(); /** + * Get the view definition associated with this context. + * + * @return the view definition + */ + public ViewDefinition getViewDefinition(); + + /** * Get the view instance name. * - * @return the view instance name + * @return the view instance name; null if no instance is associated */ public String getInstanceName(); /** + * Get the view instance definition associated with this context. + * + * @return the view instance definition; null if no instance is associated + */ + public ViewInstanceDefinition getViewInstanceDefinition(); + + /** * Get the property values specified to create the view instance. * - * @return the view instance property values + * @return the view instance property values; null if no instance is associated */ public Map<String, String> getProperties(); @@ -65,6 +80,8 @@ public interface ViewContext { * * @param key the key * @param value the value + * + * @throws IllegalStateException if no instance is associated */ public void putInstanceData(String key, String value); @@ -73,14 +90,14 @@ public interface ViewContext { * * @param key the key * - * @return the instance data value + * @return the instance data value; null if no instance is associated */ public String getInstanceData(String key); /** * Get the instance data values. * - * @return the view instance property values + * @return the view instance property values; null if no instance is associated */ public Map<String, String> getInstanceData(); @@ -88,6 +105,8 @@ public interface ViewContext { * Remove the instance data value for the given key. * * @param key the key + * + * @throws IllegalStateException if no instance is associated */ public void removeInstanceData(String key); @@ -105,7 +124,7 @@ public interface ViewContext { * * @param type the resource type * - * @return the resource provider + * @return the resource provider; null if no instance is associated */ public ResourceProvider<?> getResourceProvider(String type); @@ -119,7 +138,28 @@ public interface ViewContext { /** * Get a data store for view persistence entities. * - * @return a data store + * @return a data store; null if no instance is associated */ public DataStore getDataStore(); + + /** + * Get all of the available view definitions. + * + * @return the view definitions + */ + public Collection<ViewDefinition> getViewDefinitions(); + + /** + * Get all of the available view instance definitions. + * + * @return the view instance definitions + */ + public Collection<ViewInstanceDefinition> getViewInstanceDefinitions(); + + /** + * Get a view controller associated with this context. + * + * @return the view controller + */ + public ViewController getController(); } http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-views/src/main/java/org/apache/ambari/view/ViewController.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/ViewController.java b/ambari-views/src/main/java/org/apache/ambari/view/ViewController.java new file mode 100644 index 0000000..d182de0 --- /dev/null +++ b/ambari-views/src/main/java/org/apache/ambari/view/ViewController.java @@ -0,0 +1,56 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.view; + +import org.apache.ambari.view.events.Listener; + +import java.util.Map; + +/** + * View controller. + */ +public interface ViewController { + /** + * Notify all listeners registered to listen to this view + * of the given view event. + * + * @param eventId the event id + * @param eventProperties the event properties + */ + public void fireEvent(String eventId, Map<String, String> eventProperties); + + /** + * Register a listener to listen for events from the view identified by the + * given name. + * + * @param listener the listener + * @param viewName the view to listen to + */ + public void registerListener(Listener listener, String viewName); + + /** + * Register a listener to listen for events from the view identified by the + * given name and version. + * + * @param listener the listener + * @param viewName the view to listen to + * @param viewVersion the view version + */ + public void registerListener(Listener listener, String viewName, String viewVersion); +} http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-views/src/main/java/org/apache/ambari/view/ViewDefinition.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/ViewDefinition.java b/ambari-views/src/main/java/org/apache/ambari/view/ViewDefinition.java new file mode 100644 index 0000000..8389033 --- /dev/null +++ b/ambari-views/src/main/java/org/apache/ambari/view/ViewDefinition.java @@ -0,0 +1,46 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.view; + +/** + * The view definition. + */ +public interface ViewDefinition { + + /** + * Get the view name. + * + * @return the view name + */ + public String getViewName(); + + /** + * Get the view label (display name). + * + * @return the view label + */ + public String getLabel(); + + /** + * Get the view version. + * + * @return the version + */ + public String getVersion(); +} http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-views/src/main/java/org/apache/ambari/view/ViewInstanceDefinition.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/ViewInstanceDefinition.java b/ambari-views/src/main/java/org/apache/ambari/view/ViewInstanceDefinition.java new file mode 100644 index 0000000..58f40b0 --- /dev/null +++ b/ambari-views/src/main/java/org/apache/ambari/view/ViewInstanceDefinition.java @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.view; + +import java.util.Map; + +/** + * The view instance definition. + */ +public interface ViewInstanceDefinition { + + /** + * Get the name of this instance. + * + * @return the instance name + */ + public String getInstanceName(); + + /** + * Get the view name. + * + * @return the view name + */ + public String getViewName(); + + /** + * Get the instance property map. + * + * @return the map of instance properties + */ + public Map<String, String> getPropertyMap(); + + /** + * Get the view instance application data. + * + * @return the view instance application data map + */ + public Map<String, String> getInstanceDataMap(); + + /** + * Get the associated view definition. + * + * @return the view definition + */ + public ViewDefinition getViewDefinition(); +} http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-views/src/main/java/org/apache/ambari/view/events/Event.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/events/Event.java b/ambari-views/src/main/java/org/apache/ambari/view/events/Event.java new file mode 100644 index 0000000..503b789 --- /dev/null +++ b/ambari-views/src/main/java/org/apache/ambari/view/events/Event.java @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.view.events; + +import org.apache.ambari.view.ViewDefinition; +import org.apache.ambari.view.ViewInstanceDefinition; + +import java.util.Map; + +/** + * View event interface. + */ +public interface Event { + /** + * Get the event identifier. + * + * @return the id + */ + public String getId(); + + /** + * Get the event properties. + * + * @return the properties + */ + public Map<String, String> getProperties(); + + /** + * Get the view subject of the event. + * + * @return the view subject + */ + public ViewDefinition getViewSubject(); + + /** + * Get the instance subject of the event. + * + * @return the instance subject; null if this is a view level event + */ + public ViewInstanceDefinition getViewInstanceSubject(); +} http://git-wip-us.apache.org/repos/asf/ambari/blob/6d14390a/ambari-views/src/main/java/org/apache/ambari/view/events/Listener.java ---------------------------------------------------------------------- diff --git a/ambari-views/src/main/java/org/apache/ambari/view/events/Listener.java b/ambari-views/src/main/java/org/apache/ambari/view/events/Listener.java new file mode 100644 index 0000000..1ec586e --- /dev/null +++ b/ambari-views/src/main/java/org/apache/ambari/view/events/Listener.java @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.view.events; + +/** + * Listener interface for view events. + */ +public interface Listener { + + /** + * Notify this listener of the given event. + * + * @param event the event + */ + public void notify(Event event); +}
