aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java
blob: f2f26d980bff7620519fcc5b998360576d9b78f0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package tools.refinery.language.web.xtext.server;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.emf.common.util.URI;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.util.IDisposable;
import org.eclipse.xtext.web.server.IServiceContext;
import org.eclipse.xtext.web.server.IServiceResult;
import org.eclipse.xtext.web.server.ISession;
import org.eclipse.xtext.web.server.InvalidRequestException;
import org.eclipse.xtext.web.server.InvalidRequestException.UnknownLanguageException;
import org.eclipse.xtext.web.server.XtextServiceDispatcher;

import com.google.common.base.Strings;
import com.google.inject.Injector;

import tools.refinery.language.web.xtext.server.message.XtextWebErrorKind;
import tools.refinery.language.web.xtext.server.message.XtextWebErrorResponse;
import tools.refinery.language.web.xtext.server.message.XtextWebOkResponse;
import tools.refinery.language.web.xtext.server.message.XtextWebPushMessage;
import tools.refinery.language.web.xtext.server.message.XtextWebRequest;
import tools.refinery.language.web.xtext.server.push.PrecomputationListener;
import tools.refinery.language.web.xtext.server.push.PushWebDocument;
import tools.refinery.language.web.xtext.servlet.SimpleServiceContext;

public class TransactionExecutor implements IDisposable, PrecomputationListener {
	private final ISession session;

	private final IResourceServiceProvider.Registry resourceServiceProviderRegistry;

	private final Map<String, WeakReference<PushWebDocument>> subscriptions = new HashMap<>();

	private ResponseHandler responseHandler;

	public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) {
		this.session = session;
		this.resourceServiceProviderRegistry = resourceServiceProviderRegistry;
	}

	public void setResponseHandler(ResponseHandler responseHandler) {
		this.responseHandler = responseHandler;
	}

	public void handleRequest(XtextWebRequest request) throws ResponseHandlerException {
		var serviceContext = new SimpleServiceContext(session, request.getRequestData());
		try {
			var injector = getInjector(serviceContext);
			var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class);
			var service = serviceDispatcher.getService(new SubscribingServiceContext(serviceContext, this));
			var serviceResult = service.getService().apply();
			responseHandler.onResponse(new XtextWebOkResponse(request, serviceResult));
		} catch (InvalidRequestException e) {
			responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.REQUEST_ERROR, e));
		} catch (RuntimeException e) {
			responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.SERVER_ERROR, e));
		}
	}

	@Override
	public void onPrecomputedServiceResult(String resourceId, String stateId, String serviceName,
			IServiceResult serviceResult) throws ResponseHandlerException {
		responseHandler.onResponse(new XtextWebPushMessage(resourceId, stateId, serviceName, serviceResult));
	}

	@Override
	public void onSubscribeToPrecomputationEvents(String resourceId, PushWebDocument document) {
		PushWebDocument previousDocument = null;
		var previousSubscription = subscriptions.get(resourceId);
		if (previousSubscription != null) {
			previousDocument = previousSubscription.get();
		}
		if (previousDocument == document) {
			return;
		}
		if (previousDocument != null) {
			previousDocument.removePrecomputationListener(this);
		}
		subscriptions.put(resourceId, new WeakReference<>(document));
	}

	/**
	 * Get the injector to satisfy the request in the {@code serviceContext}.
	 * 
	 * Based on {@link org.eclipse.xtext.web.servlet.XtextServlet#getInjector}.
	 * 
	 * @param serviceContext the Xtext service context of the request
	 * @return the injector for the Xtext language in the request
	 * @throws UnknownLanguageException if the Xtext language cannot be determined
	 */
	protected Injector getInjector(IServiceContext context) {
		IResourceServiceProvider resourceServiceProvider = null;
		var resourceName = context.getParameter("resource");
		if (resourceName == null) {
			resourceName = "";
		}
		var emfURI = URI.createURI(resourceName);
		var contentType = context.getParameter("contentType");
		if (Strings.isNullOrEmpty(contentType)) {
			resourceServiceProvider = resourceServiceProviderRegistry.getResourceServiceProvider(emfURI);
			if (resourceServiceProvider == null) {
				if (emfURI.toString().isEmpty()) {
					throw new UnknownLanguageException(
							"Unable to identify the Xtext language: missing parameter 'resource' or 'contentType'.");
				} else {
					throw new UnknownLanguageException(
							"Unable to identify the Xtext language for resource " + emfURI + ".");
				}
			}
		} else {
			resourceServiceProvider = resourceServiceProviderRegistry.getResourceServiceProvider(emfURI, contentType);
			if (resourceServiceProvider == null) {
				throw new UnknownLanguageException(
						"Unable to identify the Xtext language for contentType " + contentType + ".");
			}
		}
		return resourceServiceProvider.get(Injector.class);
	}

	@Override
	public void dispose() {
		for (var subscription : subscriptions.values()) {
			var document = subscription.get();
			if (document != null) {
				document.removePrecomputationListener(this);
			}
		}
	}
}