Browse Source

AMBARI-5914 - Views: View server-side events

tbeerbower 11 years ago
parent
commit
6d14390a36

+ 45 - 19
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.ViewSubResourceDefinition;
 import org.apache.ambari.server.view.configuration.ResourceConfig;
 import org.apache.ambari.server.view.configuration.ResourceConfig;
 import org.apache.ambari.server.view.configuration.ViewConfig;
 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.Basic;
 import javax.persistence.CascadeType;
 import javax.persistence.CascadeType;
@@ -47,7 +49,7 @@ import java.util.Set;
 @NamedQuery(name = "allViews",
 @NamedQuery(name = "allViews",
     query = "SELECT view FROM ViewEntity view")
     query = "SELECT view FROM ViewEntity view")
 @Entity
 @Entity
-public class ViewEntity {
+public class ViewEntity implements ViewDefinition {
   /**
   /**
    * The unique view name.
    * The unique view name.
    */
    */
@@ -146,6 +148,12 @@ public class ViewEntity {
   @Transient
   @Transient
   private String commonName = null;
   private String commonName = null;
 
 
+  /**
+   * The view.
+   */
+  @Transient
+  private View view = null;
+
 
 
   // ----- Constructors ------------------------------------------------------
   // ----- 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 --------------------------------------------------------
   // ----- ViewEntity --------------------------------------------------------
 
 
   /**
   /**
@@ -220,15 +246,6 @@ public class ViewEntity {
     return commonName;
     return commonName;
   }
   }
 
 
-  /**
-   * Get the view label (display name).
-   *
-   * @return the view label
-   */
-  public String getLabel() {
-    return label;
-  }
-
   /**
   /**
    * Set the view label (display name).
    * Set the view label (display name).
    *
    *
@@ -238,15 +255,6 @@ public class ViewEntity {
     this.label = label;
     this.label = label;
   }
   }
 
 
-  /**
-   * Get the view version.
-   *
-   * @return the version
-   */
-  public String getVersion() {
-    return version;
-  }
-
   /**
   /**
    * Set the view version.
    * Set the view version.
    *
    *
@@ -502,6 +510,24 @@ public class ViewEntity {
     return configuration;
     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 ----------------------------------------------------
   // ----- helper methods ----------------------------------------------------
 
 

+ 38 - 35
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.controller.spi.Resource;
 import org.apache.ambari.server.view.configuration.InstanceConfig;
 import org.apache.ambari.server.view.configuration.InstanceConfig;
 import org.apache.ambari.view.ResourceProvider;
 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.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Column;
@@ -45,7 +48,7 @@ import java.util.Map;
 @NamedQuery(name = "allViewInstances",
 @NamedQuery(name = "allViewInstances",
     query = "SELECT viewInstance FROM ViewInstanceEntity viewInstance")
     query = "SELECT viewInstance FROM ViewInstanceEntity viewInstance")
 @Entity
 @Entity
-public class ViewInstanceEntity {
+public class ViewInstanceEntity implements ViewInstanceDefinition {
   /**
   /**
    * The prefix for every view instance context path.
    * 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() {
   public String getViewName() {
     return viewName;
     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.
    * Set the view name.
    *
    *
@@ -189,20 +220,6 @@ public class ViewInstanceEntity {
     return properties;
     return properties;
   }
   }
 
 
-  /**
-   * 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.
    * Add a property value to this instance.
    *
    *
@@ -293,20 +310,6 @@ public class ViewInstanceEntity {
     this.entities = entities;
     this.entities = entities;
   }
   }
 
 
-  /**
-   * 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.
    * Associate the given instance data value with the given key.
    *
    *

+ 109 - 12
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.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Injector;
 import org.apache.ambari.server.configuration.ComponentSSLConfiguration;
 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.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.DataStoreImpl;
 import org.apache.ambari.server.view.persistence.DataStoreModule;
 import org.apache.ambari.server.view.persistence.DataStoreModule;
 import org.apache.ambari.view.DataStore;
 import org.apache.ambari.view.DataStore;
 import org.apache.ambari.view.ResourceProvider;
 import org.apache.ambari.view.ResourceProvider;
 import org.apache.ambari.view.URLStreamProvider;
 import org.apache.ambari.view.URLStreamProvider;
 import org.apache.ambari.view.ViewContext;
 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.Authentication;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 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.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
 /**
 /**
  * View context implementation.
  * 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.
    * The associated view definition.
@@ -69,64 +83,94 @@ public class ViewContextImpl implements ViewContext {
   // ---- Constructors -------------------------------------------------------
   // ---- 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 viewInstanceEntity  the view entity
    * @param viewRegistry        the view registry
    * @param viewRegistry        the view registry
    */
    */
   public ViewContextImpl(ViewInstanceEntity viewInstanceEntity, ViewRegistry viewRegistry) {
   public ViewContextImpl(ViewInstanceEntity viewInstanceEntity, ViewRegistry viewRegistry) {
+    this.viewEntity         = viewInstanceEntity.getViewEntity();
     this.viewInstanceEntity = viewInstanceEntity;
     this.viewInstanceEntity = viewInstanceEntity;
     this.viewRegistry       = viewRegistry;
     this.viewRegistry       = viewRegistry;
     this.streamProvider     = ViewURLStreamProvider.getProvider();
     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 -------------------------------------------------------
   // ----- ViewContext -------------------------------------------------------
 
 
   @Override
   @Override
   public String getViewName() {
   public String getViewName() {
-    return viewInstanceEntity.getViewEntity().getCommonName();
+    return viewEntity.getCommonName();
+  }
+
+  @Override
+  public ViewDefinition getViewDefinition() {
+    return viewEntity;
   }
   }
 
 
   @Override
   @Override
   public String getInstanceName() {
   public String getInstanceName() {
-    return viewInstanceEntity.getName();
+    return viewInstanceEntity == null ? null : viewInstanceEntity.getName();
+  }
+
+  @Override
+  public ViewInstanceDefinition getViewInstanceDefinition() {
+    return viewInstanceEntity;
   }
   }
 
 
   @Override
   @Override
   public Map<String, String> getProperties() {
   public Map<String, String> getProperties() {
-    return Collections.unmodifiableMap(viewInstanceEntity.getPropertyMap());
+    return viewInstanceEntity == null ? null :
+        Collections.unmodifiableMap(viewInstanceEntity.getPropertyMap());
   }
   }
 
 
   @Override
   @Override
   public void putInstanceData(String key, String value) {
   public void putInstanceData(String key, String value) {
+    checkInstance();
     viewInstanceEntity.putInstanceData(key, value);
     viewInstanceEntity.putInstanceData(key, value);
     viewRegistry.updateViewInstance(viewInstanceEntity);
     viewRegistry.updateViewInstance(viewInstanceEntity);
   }
   }
 
 
   @Override
   @Override
   public String getInstanceData(String key) {
   public String getInstanceData(String key) {
-    return viewInstanceEntity.getInstanceDataMap().get(key);
+    return viewInstanceEntity == null ? null :
+        viewInstanceEntity.getInstanceDataMap().get(key);
   }
   }
 
 
   @Override
   @Override
   public Map<String, String> getInstanceData() {
   public Map<String, String> getInstanceData() {
-    return Collections.unmodifiableMap(viewInstanceEntity.getInstanceDataMap());
+    return viewInstanceEntity == null ? null :
+        Collections.unmodifiableMap(viewInstanceEntity.getInstanceDataMap());
   }
   }
 
 
   @Override
   @Override
   public void removeInstanceData(String key) {
   public void removeInstanceData(String key) {
+    checkInstance();
     viewRegistry.removeInstanceData(viewInstanceEntity, key);
     viewRegistry.removeInstanceData(viewInstanceEntity, key);
   }
   }
 
 
   @Override
   @Override
   public String getAmbariProperty(String key) {
   public String getAmbariProperty(String key) {
-    return viewInstanceEntity.getViewEntity().getAmbariProperty(key);
+    return viewInstanceEntity == null ? null :
+        viewInstanceEntity.getViewEntity().getAmbariProperty(key);
   }
   }
 
 
   @Override
   @Override
   public ResourceProvider<?> getResourceProvider(String type) {
   public ResourceProvider<?> getResourceProvider(String type) {
-    return viewInstanceEntity.getResourceProvider(type);
+    return viewInstanceEntity == null ? null :
+        viewInstanceEntity.getResourceProvider(type);
   }
   }
 
 
   @Override
   @Override
@@ -151,13 +195,66 @@ public class ViewContextImpl implements ViewContext {
 
 
   @Override
   @Override
   public synchronized DataStore getDataStore() {
   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;
     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 -------------------------------
   // ----- Inner class : ViewURLStreamProvider -------------------------------
 
 

+ 93 - 1
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.ResourceConfig;
 import org.apache.ambari.server.view.configuration.ViewConfig;
 import org.apache.ambari.server.view.configuration.ViewConfig;
 import org.apache.ambari.view.SystemException;
 import org.apache.ambari.view.SystemException;
+import org.apache.ambari.view.View;
 import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.ViewDefinition;
 import org.apache.ambari.view.ViewResourceHandler;
 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.eclipse.jetty.webapp.WebAppContext;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
@@ -104,6 +108,12 @@ public class ViewRegistry {
   private final Map<String, Set<SubResourceDefinition>> subResourceDefinitionsMap =
   private final Map<String, Set<SubResourceDefinition>> subResourceDefinitionsMap =
       new HashMap<String, Set<SubResourceDefinition>>();
       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.
    * Helper class.
    */
    */
@@ -168,6 +178,10 @@ public class ViewRegistry {
    * @param definition  the definition
    * @param definition  the definition
    */
    */
   public void addDefinition(ViewEntity definition) {
   public void addDefinition(ViewEntity definition) {
+    View view = definition.getView();
+    if (view != null) {
+      view.onDeploy(definition);
+    }
     viewDefinitions.put(definition.getName(), definition);
     viewDefinitions.put(definition.getName(), definition);
   }
   }
 
 
@@ -216,6 +230,11 @@ public class ViewRegistry {
       instanceDefinitions = new HashMap<String, ViewInstanceEntity>();
       instanceDefinitions = new HashMap<String, ViewInstanceEntity>();
       viewInstanceDefinitions.put(definition, instanceDefinitions);
       viewInstanceDefinitions.put(definition, instanceDefinitions);
     }
     }
+
+    View view = definition.getView();
+    if (view != null) {
+      view.onCreate(instanceDefinition);
+    }
     instanceDefinitions.put(instanceDefinition.getName(), instanceDefinition);
     instanceDefinitions.put(instanceDefinition.getName(), instanceDefinition);
   }
   }
 
 
@@ -228,7 +247,15 @@ public class ViewRegistry {
   public void removeInstanceDefinition(ViewEntity definition, String instanceName) {
   public void removeInstanceDefinition(ViewEntity definition, String instanceName) {
     Map<String, ViewInstanceEntity> instanceDefinitions = viewInstanceDefinitions.get(definition);
     Map<String, ViewInstanceEntity> instanceDefinitions = viewInstanceDefinitions.get(definition);
     if (instanceDefinitions != null) {
     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 ----------------------------------------------------
   // ----- helper methods ----------------------------------------------------
 
 
@@ -479,6 +540,7 @@ public class ViewRegistry {
     viewDefinitions.clear();
     viewDefinitions.clear();
     viewInstanceDefinitions.clear();
     viewInstanceDefinitions.clear();
     subResourceDefinitionsMap.clear();
     subResourceDefinitionsMap.clear();
+    listeners.clear();
   }
   }
 
 
   /**
   /**
@@ -562,6 +624,12 @@ public class ViewRegistry {
       }
       }
       viewDefinition.setResources(resources);
       viewDefinition.setResources(resources);
     }
     }
+    View view = null;
+    if (viewConfig.getView() != null) {
+      view = getView(viewConfig.getViewClass(cl), new ViewContextImpl(viewDefinition, this));
+    }
+    viewDefinition.setView(view);
+
     return viewDefinition;
     return viewDefinition;
   }
   }
 
 
@@ -675,6 +743,19 @@ public class ViewRegistry {
     return viewInstanceInjector.getInstance(clazz);
     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
   // remove undeployed views from the ambari db
   private void removeUndeployedViews() {
   private void removeUndeployedViews() {
     for (ViewEntity viewEntity : viewDAO.findAll()) {
     for (ViewEntity viewEntity : viewDAO.findAll()) {
@@ -824,6 +905,17 @@ public class ViewRegistry {
     return URLClassLoader.newInstance(urlList.toArray(new URL[urlList.size()]));
     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.
    * Static initialization of DAO.
    *
    *

+ 38 - 3
ambari-server/src/main/java/org/apache/ambari/server/view/configuration/ViewConfig.java

@@ -18,15 +18,14 @@
 
 
 package org.apache.ambari.server.view.configuration;
 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.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlRootElement;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.List;
-import java.util.Map;
 
 
 /**
 /**
  * View configuration.
  * View configuration.
@@ -49,6 +48,17 @@ public class ViewConfig {
    */
    */
   private String version;
   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.
    * The list of view parameters.
    */
    */
@@ -100,6 +110,31 @@ public class ViewConfig {
     return version;
     return version;
   }
   }
 
 
+  /**
+   * 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.
    * Get the list of view parameters.
    *
    *

+ 132 - 0
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;
+  }
+}

+ 55 - 0
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.ResourceConfig;
 import org.apache.ambari.server.view.configuration.ResourceConfigTest;
 import org.apache.ambari.server.view.configuration.ResourceConfigTest;
 import org.apache.ambari.server.view.configuration.ViewConfig;
 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.easymock.Capture;
 import org.junit.AfterClass;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Assert;
@@ -70,6 +74,18 @@ import static org.easymock.EasyMock.verify;
  */
  */
 public class ViewRegistryTest {
 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
   @Test
   public void testReadViewArchives() throws Exception {
   public void testReadViewArchives() throws Exception {
     Configuration configuration = createNiceMock(Configuration.class);
     Configuration configuration = createNiceMock(Configuration.class);
@@ -288,6 +304,28 @@ public class ViewRegistryTest {
         libDir, fileEntry, viewJarFile, enumeration, jarEntry, is, fos, vDAO);
         libDir, fileEntry, viewJarFile, enumeration, jarEntry, is, fos, vDAO);
   }
   }
 
 
+  @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
   @Test
   public void testAddGetDefinitions() throws Exception {
   public void testAddGetDefinitions() throws Exception {
     ViewEntity viewDefinition = ViewEntityTest.getViewEntity();
     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;
+    }
+  }
+
 }
 }

+ 12 - 0
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" +
       "    <version>1.0.0</version>\n" +
       "</view>";
       "</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
   @Test
   public void testGetName() throws Exception {
   public void testGetName() throws Exception {
@@ -119,6 +125,12 @@ public class ViewConfigTest {
     Assert.assertEquals("1.0.0", config.getVersion());
     Assert.assertEquals("1.0.0", config.getVersion());
   }
   }
 
 
+  @Test
+  public void testGetView() throws Exception {
+    ViewConfig config = getConfig(view_class_xml);
+    Assert.assertEquals("ViewImpl", config.getView());
+  }
+
   @Test
   @Test
   public void testGetParameters() throws Exception {
   public void testGetParameters() throws Exception {
     ViewConfig config = getConfig();
     ViewConfig config = getConfig();

+ 89 - 0
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);
+  }
+}

+ 46 - 0
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);
+}

+ 46 - 6
ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java

@@ -18,6 +18,7 @@
 
 
 package org.apache.ambari.view;
 package org.apache.ambari.view;
 
 
+import java.util.Collection;
 import java.util.Map;
 import java.util.Map;
 
 
 /**
 /**
@@ -46,17 +47,31 @@ public interface ViewContext {
    */
    */
   public String getViewName();
   public String getViewName();
 
 
+  /**
+   * Get the view definition associated with this context.
+   *
+   * @return the view definition
+   */
+  public ViewDefinition getViewDefinition();
+
   /**
   /**
    * Get the view instance name.
    * Get the view instance name.
    *
    *
-   * @return the view instance name
+   * @return the view instance name; null if no instance is associated
    */
    */
   public String getInstanceName();
   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.
    * 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();
   public Map<String, String> getProperties();
 
 
@@ -65,6 +80,8 @@ public interface ViewContext {
    *
    *
    * @param key    the key
    * @param key    the key
    * @param value  the value
    * @param value  the value
+   *
+   * @throws IllegalStateException if no instance is associated
    */
    */
   public void putInstanceData(String key, String value);
   public void putInstanceData(String key, String value);
 
 
@@ -73,14 +90,14 @@ public interface ViewContext {
    *
    *
    * @param key  the key
    * @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);
   public String getInstanceData(String key);
 
 
   /**
   /**
    * Get the instance data values.
    * 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();
   public Map<String, String> getInstanceData();
 
 
@@ -88,6 +105,8 @@ public interface ViewContext {
    * Remove the instance data value for the given key.
    * Remove the instance data value for the given key.
    *
    *
    * @param key  the key
    * @param key  the key
+   *
+   * @throws IllegalStateException if no instance is associated
    */
    */
   public void removeInstanceData(String key);
   public void removeInstanceData(String key);
 
 
@@ -105,7 +124,7 @@ public interface ViewContext {
    *
    *
    * @param type  the resource type
    * @param type  the resource type
    *
    *
-   * @return the resource provider
+   * @return the resource provider; null if no instance is associated
    */
    */
   public ResourceProvider<?> getResourceProvider(String type);
   public ResourceProvider<?> getResourceProvider(String type);
 
 
@@ -119,7 +138,28 @@ public interface ViewContext {
   /**
   /**
    * Get a data store for view persistence entities.
    * 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();
   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();
 }
 }

+ 56 - 0
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);
+}

+ 46 - 0
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();
+}

+ 62 - 0
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();
+}

+ 57 - 0
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();
+}

+ 32 - 0
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);
+}