Bug 414566 - Add the possibility to listen to commits on the server side
https://bugs.eclipse.org/bugs/show_bug.cgi?id=414566

Added ESCallObserver extension point and notification for each server call
diff --git a/bundles/org.eclipse.emf.emfstore.server/plugin.xml b/bundles/org.eclipse.emf.emfstore.server/plugin.xml
index eb579cb..51f6eac 100644
--- a/bundles/org.eclipse.emf.emfstore.server/plugin.xml
+++ b/bundles/org.eclipse.emf.emfstore.server/plugin.xml
@@ -3,7 +3,8 @@
 <plugin>

    <extension-point id="locationProvider" name="Location Provider" schema="schema/locationProvider.exsd"/>

    <extension-point id="configurationResource" name="Server Configuration Resources" schema="schema/configurationResource.exsd"/>

-   <extension-point id="computeChecksum" name="Compute Checksum on Commit" schema="schema/computeChecksum.exsd"/>   

+   <extension-point id="computeChecksum" name="Compute Checksum on Commit" schema="schema/computeChecksum.exsd"/>

+   <extension-point id="serverCallObserver" name="Observer all calls to the server" schema="schema/serverCallObserver.exsd"/>   

    <extension

          id="application"

          name="EMFStore server"

@@ -31,5 +32,5 @@
                value="EMFStore Server">

          </property>

       </product>

-   </extension>

+   </extension>
 </plugin>

diff --git a/bundles/org.eclipse.emf.emfstore.server/schema/serverCallObserver.exsd b/bundles/org.eclipse.emf.emfstore.server/schema/serverCallObserver.exsd
new file mode 100644
index 0000000..e2a7303
--- /dev/null
+++ b/bundles/org.eclipse.emf.emfstore.server/schema/serverCallObserver.exsd
@@ -0,0 +1,91 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.emf.emfstore.server" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.emf.emfstore.server" id="serverCallObserver" name="Observer all calls to the server"/>
+      </appInfo>
+      <documentation>
+         Allows to observe all calls to the server and their method name and arguments. Observers are notified before and after the call. After the call the result is also provided to the observer.
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <annotation>
+         <appInfo>
+            <meta.element />
+         </appInfo>
+      </annotation>
+      <complexType>
+         <choice>
+            <element ref="ServerCallObserver" minOccurs="1" maxOccurs="unbounded"/>
+         </choice>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="ServerCallObserver">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn=":org.eclipse.emf.emfstore.server.observer.ESServerCallObserver"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         1.0.1
+      </documentation>
+   </annotation>
+
+
+
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         Copyright (c) 2011-2013 EclipseSource Muenchen GmbH and others.&lt;br/&gt;
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/epl-v10.html
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/EMFStoreImpl.java b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/EMFStoreImpl.java
index 936a434..e12c998 100644
--- a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/EMFStoreImpl.java
+++ b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/EMFStoreImpl.java
@@ -16,7 +16,12 @@
 import java.lang.reflect.Method;

 import java.lang.reflect.Proxy;

 import java.util.EnumMap;

+import java.util.LinkedHashSet;

+import java.util.Set;

 

+import org.eclipse.emf.emfstore.common.extensionpoint.ESExtensionElement;

+import org.eclipse.emf.emfstore.common.extensionpoint.ESExtensionPoint;

+import org.eclipse.emf.emfstore.internal.common.model.util.ModelUtil;

 import org.eclipse.emf.emfstore.internal.server.EMFStore;

 import org.eclipse.emf.emfstore.internal.server.accesscontrol.AuthorizationControl;

 import org.eclipse.emf.emfstore.internal.server.core.helper.EmfStoreMethod;

@@ -32,6 +37,7 @@
 import org.eclipse.emf.emfstore.internal.server.exceptions.FatalESException;

 import org.eclipse.emf.emfstore.internal.server.model.ServerSpace;

 import org.eclipse.emf.emfstore.server.exceptions.ESException;

+import org.eclipse.emf.emfstore.server.observer.ESServerCallObserver;

 

 /**

  * This is the main implementation of {@link EMFStore}.

@@ -48,11 +54,11 @@
 	 * @author boehlke

 	 */

 	private class SubInterfaceMethod {

-		private AbstractSubEmfstoreInterface iface;

-		private Method method;

+		private final AbstractSubEmfstoreInterface iface;

+		private final Method method;

 

 		public SubInterfaceMethod(AbstractSubEmfstoreInterface iface, Method m) {

-			this.method = m;

+			method = m;

 			this.iface = iface;

 		}

 

@@ -72,6 +78,7 @@
 	}

 

 	private EnumMap<MethodId, SubInterfaceMethod> subInterfaceMethods;

+	private final Set<ESServerCallObserver> serverCallObservers;

 

 	/**

 	 * Default constructor.

@@ -86,6 +93,22 @@
 	public EMFStoreImpl(ServerSpace serverSpace, AuthorizationControl authorizationControl)

 		throws FatalESException {

 		super(serverSpace, authorizationControl);

+		serverCallObservers = initServerCallObservers();

+	}

+

+	/**

+	 * 

+	 */

+	private Set<ESServerCallObserver> initServerCallObservers() {

+		final Set<ESServerCallObserver> result = new LinkedHashSet<ESServerCallObserver>();

+		for (final ESExtensionElement e : new ESExtensionPoint("org.eclipse.emf.emfstore.server.serverCallObserver")

+			.getExtensionElements()) {

+			final ESServerCallObserver observer = e.getClass("class", ESServerCallObserver.class);

+			if (observer != null) {

+				result.add(observer);

+			}

+		}

+		return result;

 	}

 

 	/**

@@ -107,8 +130,8 @@
 	@Override

 	protected void addSubInterface(AbstractSubEmfstoreInterface iface) {

 		super.addSubInterface(iface);

-		for (Method method : iface.getClass().getMethods()) {

-			EmfStoreMethod implSpec = method.getAnnotation(EmfStoreMethod.class);

+		for (final Method method : iface.getClass().getMethods()) {

+			final EmfStoreMethod implSpec = method.getAnnotation(EmfStoreMethod.class);

 			if (implSpec != null) {

 				subInterfaceMethods.put(implSpec.value(), new SubInterfaceMethod(iface, method));

 			}

@@ -121,12 +144,59 @@
 	 * 

 	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])

 	 */

-	public Object invoke(Object obj, Method method, Object[] args) throws ESException {

-		MethodInvocation methodInvocation = new MethodInvocation(method.getName(), args);

-

+	public Object invoke(Object obj, final Method method, final Object[] args) throws ESException {

+		final MethodInvocation methodInvocation = new MethodInvocation(method.getName(), args);

 		getAuthorizationControl().checkAccess(methodInvocation);

-		SubInterfaceMethod subIfaceMethod = subInterfaceMethods.get(methodInvocation.getType());

-		return subIfaceMethod.getIface().execute(subIfaceMethod.getMethod(), args);

+

+		notifyServerCallObservers(new ServerCallObserverNotifier() {

+			public void notify(ESServerCallObserver observer) {

+				observer.notifyPreServerCallExecution(method, args);

+			}

+		});

+		final SubInterfaceMethod subIfaceMethod = subInterfaceMethods.get(methodInvocation.getType());

+		try {

+			final Object result = subIfaceMethod.getIface().execute(subIfaceMethod.getMethod(), args);

+			notifyServerCallObservers(new ServerCallObserverNotifier() {

+				public void notify(ESServerCallObserver observer) {

+					observer.notifyPostServerCallExecution(method, args, result);

+				}

+			});

+			return result;

+			// notify observers about exceptions and rethrow exception

+		} catch (final ESException esException) {

+			notifyServerCallObservers(new ServerCallObserverNotifier() {

+				public void notify(ESServerCallObserver observer) {

+					observer.notifyServerCallExecutionESExceptionFailure(method, args, esException);

+				}

+			});

+			throw esException;

+			// BEGIN SUPRESS CATCH EXCEPTION

+		} catch (final RuntimeException runtimeException) {

+			// END SUPRESS CATCH EXCEPTION

+			notifyServerCallObservers(new ServerCallObserverNotifier() {

+				public void notify(ESServerCallObserver observer) {

+					observer.notifyServerCallExecutionRuntimeExceptionFailure(method, args, runtimeException);

+				}

+			});

+			throw runtimeException;

+		}

+	}

+

+	/**

+	 * Notify the observers with the given notifier.

+	 * 

+	 * @param serverCallObserverNotifier the notifier

+	 */

+	private void notifyServerCallObservers(ServerCallObserverNotifier serverCallObserverNotifier) {

+		for (final ESServerCallObserver callObserver : serverCallObservers) {

+			try {

+				serverCallObserverNotifier.notify(callObserver);

+				// BEGIN SUPRESS CATCH EXCEPTION

+			} catch (final RuntimeException runtimeException) {

+				// END SUPRESS CATCH EXCEPTION

+				ModelUtil.logWarning("Server Call Observer Notification failed with exception!", runtimeException);

+			}

+		}

 	}

 

 	/**

diff --git a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/ServerCallObserverNotifier.java b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/ServerCallObserverNotifier.java
new file mode 100644
index 0000000..633b217
--- /dev/null
+++ b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/internal/server/core/ServerCallObserverNotifier.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2013 EclipseSource Muenchen GmbH and others.
+ * 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ * User - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emf.emfstore.internal.server.core;
+
+import org.eclipse.emf.emfstore.server.observer.ESServerCallObserver;
+
+/**
+ * Notification for a ServerCallObserver.
+ * 
+ * @author mkoegel
+ * 
+ */
+public interface ServerCallObserverNotifier {
+
+	/**
+	 * Notify given {@link ESServerCallObserver}.
+	 * 
+	 * @param observer the observer to be notified
+	 */
+	void notify(ESServerCallObserver observer);
+
+}
diff --git a/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/server/observer/ESServerCallObserver.java b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/server/observer/ESServerCallObserver.java
new file mode 100644
index 0000000..0967bc8
--- /dev/null
+++ b/bundles/org.eclipse.emf.emfstore.server/src/org/eclipse/emf/emfstore/server/observer/ESServerCallObserver.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2013 EclipseSource Muenchen GmbH and others.
+ * 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ * User - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emf.emfstore.server.observer;
+
+import java.lang.reflect.Method;
+
+import org.eclipse.emf.emfstore.server.exceptions.ESException;
+
+/**
+ * 
+ * @author mkoegel
+ * @since 1.0.1
+ */
+public interface ESServerCallObserver {
+
+	/**
+	 * Notify the observer about a server call execution BEFORE it actually occurred.
+	 * 
+	 * @param method the called server method in {@link org.eclipse.emf.emfstore.internal.server.EMFStore}
+	 * @param args the method arguments.
+	 */
+	void notifyPreServerCallExecution(Method method, Object[] args);
+
+	/**
+	 * Notify the observer about a server call execution failure with a runtime exception.
+	 * 
+	 * @param method the called server method in {@link org.eclipse.emf.emfstore.internal.server.EMFStore}
+	 * @param args the method arguments.
+	 * @param runtimeException the runtime exception that occurred
+	 */
+	void notifyServerCallExecutionRuntimeExceptionFailure(Method method, Object[] args,
+		RuntimeException runtimeException);
+
+	/**
+	 * Notify the observer about a server call execution failure with a EMFStore specific exception.
+	 * 
+	 * @param method the called server method in {@link org.eclipse.emf.emfstore.internal.server.EMFStore}
+	 * @param args the method arguments.
+	 * @param exception the EMFStore specific exception that occurred
+	 */
+	void notifyServerCallExecutionESExceptionFailure(Method method, Object[] args, ESException exception);
+
+	/**
+	 * Notify the observer about a server call execution AFTER it occurred.
+	 * 
+	 * @param method the called server method in {@link org.eclipse.emf.emfstore.internal.server.EMFStore}
+	 * @param args the method arguments.
+	 * @param result the result
+	 */
+	void notifyPostServerCallExecution(Method method, Object[] args, Object result);
+
+}