Skip to content
Snippets Groups Projects
Commit b0e455ec authored by Hallvard Trætteberg's avatar Hallvard Trætteberg
Browse files

OSGi+EMF example

parent 3ce3bd6d
No related branches found
No related tags found
No related merge requests found
Showing
with 1247 additions and 0 deletions
......@@ -58,6 +58,18 @@
pretty = true; \
local = ${build}/release
-plugin.EMF: aQute.bnd.repository.p2.provider.P2Repository; \
name = "EMF 2.19 Release"; \
url = "http://download.eclipse.org/modeling/emf/emf/builds/release/2.19"
-plugin.Acceleo: aQute.bnd.repository.p2.provider.P2Repository; \
name = "Acceleo Updates 3.7 Release"; \
url = "https://download.eclipse.org/acceleo/updates/releases/3.7"
-plugin.Ra: aQute.bnd.repository.p2.provider.P2Repository; \
name = "Local Ra repository"; \
url = "file:${build}/../../tdt4250.ra.repository"
-releaserepo: Release
-baselinerepo: Release
......
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
<classpathentry kind="src" output="bin" path="src"/>
<classpathentry kind="src" output="bin_test" path="test">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
-buildpath: \
osgi.annotation;version=7.0.0,\
osgi.core;version=7.0.0,\
osgi.cmpn;version=7.0.0,\
org.apache.felix.http.servlet-api;version=1.1.2,\
org.eclipse.emf.common,\
org.eclipse.emf.ecore,\
org.eclipse.emf.ecore.xmi,\
com.fasterxml.jackson.core.jackson-annotations,\
com.fasterxml.jackson.core.jackson-core,\
com.fasterxml.jackson.core.jackson-databind,\
tdt4250.emf.servletsupport;version=latest
-testpath: \
${junit},\
${mockito},\
${mockito-deps}
javac.source: 1.8
javac.target: 1.8
Bundle-Version: 0.0.0.${tstamp}
\ No newline at end of file
-runfw: org.eclipse.osgi;version=3.13
-runee: JavaSE-1.8
-runprovidedcapabilities: ${native_capability}
-resolve.effective: active
-runproperties: \
org.osgi.service.http.port=8080,\
osgi.console=,\
osgi.console.enable.builtin=false
-runrequires: \
osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\
osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\
bnd.identity;id='tdt4250.emf.servletsupport.impl',\
bnd.identity;id='tdt4250.ra.resource',\
bnd.identity;id='tdt4250.emf.servlet'
-runbundles: \
com.fasterxml.jackson.core.jackson-annotations;version='[2.9.8,2.9.9)',\
com.fasterxml.jackson.core.jackson-core;version='[2.9.8,2.9.9)',\
com.fasterxml.jackson.core.jackson-databind;version='[2.9.8,2.9.9)',\
org.antlr.runtime;version='[4.3.0,4.3.1)',\
org.apache.felix.gogo.command;version='[1.0.2,1.0.3)',\
org.apache.felix.gogo.runtime;version='[1.0.10,1.0.11)',\
org.apache.felix.gogo.shell;version='[1.0.0,1.0.1)',\
org.apache.felix.http.jetty;version='[4.0.6,4.0.7)',\
org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
org.apache.felix.scr;version='[2.1.10,2.1.11)',\
org.eclipse.acceleo.annotations;version='[6.0.4,6.0.5)',\
org.eclipse.acceleo.query;version='[6.0.8,6.0.9)',\
org.eclipse.emf.common;version='[2.16.0,2.16.1)',\
org.eclipse.emf.ecore;version='[2.19.0,2.19.1)',\
org.eclipse.emf.ecore.xmi;version='[2.16.0,2.16.1)',\
org.eclipse.emf.gwt.common;version='[2.12.0,2.12.1)',\
tdt4250.emf.servlet;version=snapshot,\
tdt4250.emf.servletsupport;version=snapshot,\
tdt4250.emf.servletsupport.impl;version=snapshot,\
tdt4250.ra;version='[0.1.1,0.1.2)',\
tdt4250.ra.resource;version=snapshot
\ No newline at end of file
package tdt4250.ra.servlet;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Scanner;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletName;
import org.osgi.service.http.whiteboard.propertytypes.HttpWhiteboardServletPattern;
import org.osgi.service.log.Logger;
import org.osgi.service.log.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ValueNode;
import tdt4250.servletsupport.IPostHandler;
import tdt4250.servletsupport.IRequestPathResolver;
import tdt4250.servletsupport.IRequestQueryExecutor;
import tdt4250.servletsupport.IResourceProvider;
import tdt4250.servletsupport.IResponseSerializer;
@SuppressWarnings("serial")
@Component
@HttpWhiteboardServletName("data")
@HttpWhiteboardServletPattern("/data/*")
public class ResourceServlet extends HttpServlet implements Servlet {
@Reference(policy=ReferencePolicy.DYNAMIC)
protected volatile Collection<IResourceProvider> resourceProviders;
protected Optional<IResourceProvider> getResourceProvider(final String name) {
return resourceProviders.stream().filter(rp -> rp.getName().equals(name)).findFirst();
}
@Reference(policy=ReferencePolicy.DYNAMIC)
protected volatile IRequestPathResolver requestPathResolver;
@Reference(policy=ReferencePolicy.DYNAMIC)
protected volatile IRequestQueryExecutor requestQueryExecutor;
@Reference(policy=ReferencePolicy.DYNAMIC)
protected volatile IResponseSerializer responseSerializer;
@Reference(policy=ReferencePolicy.DYNAMIC)
private volatile LoggerFactory loggerFactory;
protected LoggerFactory getLoggerFactory() {
return loggerFactory;
}
protected void doHelper(HttpServletRequest req, RequestData requestData, Writer responseWriter) throws Exception {
Object result = getPathObject(requestData.resourceProvider, requestData.resourcePath, requestData.op, requestData.params);
Object postBody = requestData.params.get("httpPostBody");
if (postBody != null) {
String contentType = req.getHeader("Content-Type");
if (contentType == null) {
throw new ServletException("No Content-Type header");
}
int pos = contentType.indexOf(';');
if (pos > 0) {
contentType = contentType.substring(0, pos);
}
IPostHandler postHandler = getPostHandler(contentType);
if (postHandler == null) {
throw new ServletException("No handler for " + contentType);
}
try {
result = postHandler.handlePostBody(result, String.valueOf(postBody), requestData.params);
} catch (Exception e) {
throw new ServletException("Exception when handling post body for " + contentType);
}
}
responseSerializer.serialize(result, responseWriter);
}
protected IPostHandler getPostHandler(String contentType) {
BundleContext bundleContext = FrameworkUtil.getBundle(getClass()).getBundleContext();
Collection<ServiceReference<IPostHandler>> serviceReferences = Collections.emptyList();
try {
serviceReferences = bundleContext.getServiceReferences(IPostHandler.class, "(postHandlerMimeType=" + contentType + ")");
} catch (InvalidSyntaxException e) {
}
for (ServiceReference<IPostHandler> serviceReference : serviceReferences) {
IPostHandler postHandler = bundleContext.getService(serviceReference);
if (postHandler != null) {
return postHandler;
}
}
return null;
}
protected Logger logger;
public Logger getLogger() {
LoggerFactory loggerFactory = getLoggerFactory();
if (logger == null && loggerFactory != null) {
logger = loggerFactory.getLogger(this.getClass());
} else if (loggerFactory == null) {
logger = null;
}
return logger;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Map<String, Object> params = decodeQuery(req, new HashMap<String, Object>());
doHelper(req, resp, params);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Map<String, Object> params = decodeQuery(req, new HashMap<String, Object>());
handlePostBody(req, params, resp);
}
protected void handlePostBody(HttpServletRequest req, Map<String, Object> params, HttpServletResponse resp) throws ServletException, IOException {
try {
String body = getPostBody(req, params);
handlePostBody(body, params);
doHelper(req, resp, params);
} catch (Exception e) {
throw new ServletException(e);
}
}
protected void handlePostBody(String body, Map<String, Object> params) {
params.put("httpPostBody", body);
decodePostBody(body, params);
}
protected static class RequestData {
IResourceProvider resourceProvider;
List<String> resourcePath;
String op;
Map<String, Object> params;
}
protected RequestData getRequestData(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> params) throws ServletException, IOException {
String path = req.getPathInfo();
List<String> segments = (path != null && path.length() > 0 ? Arrays.asList(path.substring(1).split("/")) : null);
if (segments == null || segments.isEmpty()) {
throw new ServletException("Path must at least have a resource identifier");
}
String resourceProviderName = segments.get(0);
Optional<IResourceProvider> resourceProvider = getResourceProvider(resourceProviderName);
if (! resourceProvider.isPresent()) {
if (getLogger() != null) {
getLogger().info("No resource provider for " + resourceProviderName);
}
throw new ServletException("The resource identifier " + resourceProviderName + " is not recognized");
}
List<String> resourcePath = segments.subList(1, segments.size());
String op = null;
if (segments.size() > 1 && requestQueryExecutor != null) {
op = segments.get(segments.size() - 1);
resourcePath = segments.subList(1, segments.size() - 1);
}
if (getLogger() != null) {
getLogger().info("Handling " + resourcePath + " + " + op + " with " + resourceProvider);
}
try {
RequestData requestData = new RequestData();
requestData.resourceProvider = resourceProvider.get();
requestData.resourcePath = resourcePath;
requestData.op = op;
requestData.params = params;
return requestData;
} catch (Exception e) {
String message = "Exception during request handling: " + e.getMessage();
if (getLogger() != null) {
getLogger().warn(message);
}
throw new ServletException(e);
}
}
protected void doHelper(HttpServletRequest req, HttpServletResponse resp, Map<String, Object> params) throws ServletException, IOException {
RequestData requestData = getRequestData(req, resp, params);
if (requestData != null) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Writer writer = new OutputStreamWriter(outputStream);
doHelper(req, requestData, writer);
String responseString = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
resp.getWriter().write(responseString);
} catch (ServletException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}
}
protected Object getPathObject(IResourceProvider resourceProvider, Collection<String> resourcePath, String op, Map<String, Object> params) throws Exception {
Object object = requestPathResolver.getObjectForPath(resourceProvider.getRootObjects(), resourcePath.toArray(new String[0]));
Object result = object;
if (op != null) {
Collection<?> target = (object instanceof Collection<?> ? (Collection<?>) object : Collections.singletonList(object));
result = requestQueryExecutor.getRequestQueryResult(target, op, params);
}
return result;
}
//
public static String getPostBody(HttpServletRequest req, Map<String, Object> params) throws IOException {
StringBuilder buffer = new StringBuilder();
Scanner scanner = new Scanner(req.getInputStream());
try {
while (scanner.hasNextLine()) {
buffer.append(scanner.nextLine());
buffer.append("\n");
}
} finally {
scanner.close();
}
return buffer.toString();
}
public static Map<String, Object> decodeQuery(HttpServletRequest req, Map<String, Object> params) {
String query = req.getQueryString();
if (query != null) {
for (String param : query.split("&")) {
int pos = param.indexOf('=');
if (pos > 0) {
params.put(param.substring(0, pos), param.substring(pos + 1));
} else {
params.put(param, true);
}
}
}
return params;
}
public static Map<String, Object> decodePostBody(String body, Map<String, Object> params) {
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonTree = null;
try {
jsonTree = mapper.readTree(body);
} catch (IOException e) {
}
if (jsonTree instanceof ObjectNode) {
ObjectNode objectNode = (ObjectNode) jsonTree;
Iterator<String> fieldNames = objectNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
Object valueNode = objectNode.get(fieldName);
if (valueNode instanceof ValueNode) {
valueNode = ((ValueNode) valueNode).asText();
}
params.put(fieldName, valueNode);
}
}
return params;
}
}
/*
* @startuml
* interface ILogger {
* void log(int severity, String message)
* }
* class SMSLogger {
* }
* class FileLogger {
* }
* ILogger <|.. SMSLogger
* ILogger <|.. FileLogger
* @enduml
* @startuml
* class SMSLogger {
* }
* class FileLogger {
* }
* SMSLogger -() ILogger
* FileLogger -() ILogger
* @enduml
* @startuml
* [SMSLogger] -- ILogger
* [FileLogger] -- ILogger
* @enduml
* @startuml
* class HttpServerImpl {
* }
* interface Servlet {
* }
* class DataServlet {
* }
* class AppServlet {
* }
* Servlet <|.. DataServlet
* Servlet <|.. AppServlet
* HttpServerImpl -> Servlet: servlets
* @enduml
* @startuml
* [DataServlet] -- Servlet
* [AppServlet] -- Servlet
* Servlet <.. [HttpServerImpl]: use
* @enduml
*/
# To make sure the folder exists in git
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
<classpathentry kind="src" output="bin" path="src"/>
<classpathentry kind="src" output="bin_test" path="test">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
-buildpath: \
osgi.annotation;version=7.0.0,\
osgi.core;version=7.0.0,\
osgi.cmpn;version=7.0.0,\
org.eclipse.emf.common,\
org.eclipse.emf.ecore,\
com.fasterxml.jackson.core.jackson-annotations,\
com.fasterxml.jackson.core.jackson-core,\
com.fasterxml.jackson.core.jackson-databind,\
tdt4250.emf.servletsupport;version=latest,\
org.eclipse.emf.ecore.xmi
-testpath: \
${junit},\
${mockito},\
${mockito-deps}
javac.source: 1.8
javac.target: 1.8
Bundle-Version: 0.0.0.${tstamp}
\ No newline at end of file
-runfw: org.eclipse.osgi;version=3.13
-runprovidedcapabilities: ${native_capability}
-resolve.effective: active
-runproperties: \
org.osgi.service.http.port=8080,\
osgi.console=,\
osgi.console.enable.builtin=false
-runbundles:\
org.apache.felix.gogo.runtime,\
org.apache.felix.gogo.shell,\
org.apache.felix.gogo.command
-runrequires:\
osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.shell)',\
osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)'
package tdt4250.servletsupport.impl;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.util.EcoreUtil;
public class AnnotationUtil {
public static boolean includeElement(EModelElement element, String annotationKey, boolean defaultInclude) {
String include = EcoreUtil.getAnnotation(element, annotationKey, "include"), exclude = EcoreUtil.getAnnotation(element, annotationKey, "exclude");
if (defaultInclude) {
return (include == null || Boolean.valueOf(include)) && (exclude == null || (! Boolean.valueOf(exclude)));
} else {
return (include != null && Boolean.valueOf(include)) || (exclude != null && (! Boolean.valueOf(exclude)));
}
}
public static boolean excludeElement(EModelElement element, String annotationKey) {
return AnnotationUtil.includeElement(element, annotationKey, false);
}
public static boolean includeElement(EModelElement element, String annotationKey) {
return AnnotationUtil.includeElement(element, annotationKey, true);
}
public static <T extends ETypedElement> boolean includeTypedElement(T element, String annotationKey, boolean defaultInclude) {
boolean explicitInclude = AnnotationUtil.includeElement(element, annotationKey, defaultInclude);
if (explicitInclude != defaultInclude) {
return explicitInclude;
}
EClassifier type = element.getEType();
if (type != null) {
explicitInclude = AnnotationUtil.includeElement(type, annotationKey, defaultInclude);
if (explicitInclude != defaultInclude) {
return explicitInclude;
}
EPackage ePackage = type.getEPackage();
explicitInclude = AnnotationUtil.includeElement(ePackage, annotationKey, defaultInclude);
if (explicitInclude != defaultInclude) {
return explicitInclude;
}
}
return defaultInclude;
}
}
package tdt4250.servletsupport.impl;
import java.io.IOException;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import com.fasterxml.jackson.core.JsonGenerator;
public interface JsonEAttributeSerializer {
public boolean accept(EObject eObject, EAttribute attr, Object value);
public void serialize(EObject eObject, EAttribute attr, Object value, String altFieldName, JsonGenerator generator) throws IOException;
}
package tdt4250.servletsupport.impl;
import java.io.IOException;
import org.eclipse.emf.ecore.EObject;
import com.fasterxml.jackson.core.JsonGenerator;
public interface JsonEObjectSerializer {
public boolean accept(EObject eObject);
public void serialize(EObject eObject, JsonGenerator generator) throws IOException;
}
package tdt4250.servletsupport.impl;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.Stack;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferencePolicy;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import tdt4250.servletsupport.IResponseSerializer;
@SuppressWarnings("serial")
@Component
public class JsonSerializer extends StdSerializer<EObject> implements IResponseSerializer {
private ObjectMapper objectMapper;
public JsonSerializer() {
super(EObject.class);
SimpleModule module = new SimpleModule();
module.addSerializer(EObject.class, this);
this.objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
}
@Reference(policy=ReferencePolicy.DYNAMIC)
protected volatile Collection<JsonEObjectSerializer> jsonEObjectSerializers;
protected JsonEObjectSerializer getEObjectSerializer(EObject eObject) {
if (jsonEObjectSerializers != null) {
for (JsonEObjectSerializer serializer : jsonEObjectSerializers) {
if (serializer.accept(eObject)) {
return serializer;
}
}
}
return null;
}
@Reference(policy=ReferencePolicy.DYNAMIC)
protected volatile Collection<JsonEAttributeSerializer> jsonEAttributeSerializers;
protected JsonEAttributeSerializer getEAttributeSerializer(EObject eObject, EAttribute attr, Object value) {
if (jsonEAttributeSerializers != null) {
for (JsonEAttributeSerializer serializer : jsonEAttributeSerializers) {
if (serializer.accept(eObject, attr, value)) {
return serializer;
}
}
}
return null;
}
private Stack<EObject> occurStack = new Stack<EObject>();
@Override
public void serialize(Object object, Writer writer) throws Exception {
try {
this.occurStack.clear();
ObjectWriter objectWriter = objectMapper.writer(new DefaultPrettyPrinter());
objectWriter.writeValue(writer, object);
} finally {
this.occurStack.clear();
}
}
public static final String JSON_SERIALIZER_ANNOTATION_SOURCE = JsonSerializer.class.getName();
@Override
public void serialize(EObject eObject, JsonGenerator generator, SerializerProvider serializerProvider) throws IOException, JsonGenerationException {
// int count = 0;
// for (int i = occurStack.size() - 1; i >= 0; i--) {
// if (occurStack.get(i) == eObject) {
// count++;
// if (count >= 2) {
// generator.writeString("...");
// return;
// }
// }
// }
for (int i = occurStack.size() - 1; i >= 0; i--) {
if (occurStack.get(i) == eObject) {
generator.writeString("???");
return;
}
}
occurStack.push(eObject);
try {
JsonEObjectSerializer eObjectSerializer = getEObjectSerializer(eObject);
if (eObjectSerializer != null) {
eObjectSerializer.serialize(eObject, generator);
} else {
try {
generator.writeStartObject();
for (EStructuralFeature feature : eObject.eClass().getEAllStructuralFeatures()) {
if (includeFeature(feature)) {
serializeFeature(eObject, feature, generator);
}
}
for (EOperation op : eObject.eClass().getEAllOperations()) {
if (includeOperation(op)) {
serializeOperation(eObject, op, generator);
}
}
} finally {
generator.writeEndObject();
}
}
} finally {
// System.err.println("Exception while serializing " + eObject + ":" + e);
if (! occurStack.isEmpty()) {
occurStack.pop();
}
}
}
protected boolean includeFeature(EStructuralFeature feature) {
boolean include = false;
if (feature instanceof EReference) {
EReference ref = (EReference) feature;
if (ref.isContainment()) {
include = AnnotationUtil.includeTypedElement(feature, JSON_SERIALIZER_ANNOTATION_SOURCE, true);
} else if (! ref.isContainer()) {
include = AnnotationUtil.includeTypedElement(feature, JSON_SERIALIZER_ANNOTATION_SOURCE, false);
}
} else {
include = AnnotationUtil.includeTypedElement(feature, JSON_SERIALIZER_ANNOTATION_SOURCE, true);
}
return include;
}
private boolean excludeNullValues = true, excludeEmptyManyValues = true;
protected void serializeFeature(EObject eObject, EStructuralFeature feature, JsonGenerator generator) throws IOException {
String name = getFieldName(feature);
Object value = null;
try {
value = eObject.eGet(feature);
if (feature instanceof EAttribute) {
EAttribute attr = (EAttribute) feature;
JsonEAttributeSerializer attrSerializer = getEAttributeSerializer(eObject, attr, value);
if (attrSerializer != null) {
attrSerializer.serialize(eObject, attr, value, name, generator);
return;
}
}
} catch (Exception e) {
value = "Exception when getting/serializing value of " + feature.getName() + ": " + e.getMessage();
}
if (excludeNullValues && value == null) {
return;
}
if (excludeEmptyManyValues && feature.isMany() && value instanceof Collection<?> && ((Collection<?>) value).isEmpty()) {
return;
}
generator.writeFieldName(name);
generator.writeObject(value);
}
protected boolean includeOperation(EOperation op) {
return op.getEParameters().isEmpty() && AnnotationUtil.includeTypedElement(op, JSON_SERIALIZER_ANNOTATION_SOURCE, false);
}
protected void serializeOperation(EObject eObject, EOperation op, JsonGenerator generator) throws IOException {
String name = getFieldName(op);
Object value = null;
try {
value = eObject.eInvoke(op, null);
} catch (Exception e) {
value = "Exception when invoking " + op.getName() + ": " + e.getMessage();
}
if (value != null) {
generator.writeFieldName(name);
generator.writeObject(value);
}
}
protected String getFieldName(ENamedElement named) {
String altName = EcoreUtil.getAnnotation(named, JSON_SERIALIZER_ANNOTATION_SOURCE, "name");
return altName != null ? altName : named.getName();
}
}
package tdt4250.servletsupport.impl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.osgi.service.component.annotations.Component;
import tdt4250.servletsupport.IReferenceResolver;
@Component
public class NameReferenceResolver implements IReferenceResolver {
@Override
public EObject resolveReference(String reference, EObject context) {
for (EObject contained : context.eContents()) {
EStructuralFeature nameFeature = contained.eClass().getEStructuralFeature("name");
if (nameFeature != null) {
Object name = contained.eGet(nameFeature);
if (reference.equals(String.valueOf(name))) {
return contained;
}
}
}
return null;
}
}
package tdt4250.servletsupport.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.ecore.EObject;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import tdt4250.servletsupport.IReferenceResolver;
import tdt4250.servletsupport.IRequestPathResolver;
import tdt4250.servletsupport.IRequestQueryExecutor;
@Component(
service={IRequestPathResolver.class, IRequestQueryExecutor.class}
)
public class RequestHandler implements IRequestPathResolver, IRequestQueryExecutor, IReferenceResolver {
protected Collection<IReferenceResolver> referenceResolvers;
@Reference(
cardinality=ReferenceCardinality.MULTIPLE,
policy=ReferencePolicy.DYNAMIC,
unbind="removeReferenceResolver"
)
public void addReferenceResolver(IReferenceResolver referenceResolver) {
if (referenceResolvers == null) {
referenceResolvers = new ArrayList<IReferenceResolver>();
}
referenceResolvers.add(referenceResolver);
}
public void removeReferenceResolver(IReferenceResolver referenceResolver) {
if (referenceResolvers != null) {
referenceResolvers.remove(referenceResolver);
}
}
@Override
public EObject resolveReference(String reference, EObject context) {
for (IReferenceResolver referenceResolver : referenceResolvers) {
EObject resolved = referenceResolver.resolveReference(reference, context);
if (resolved != null) {
return resolved;
}
}
return null;
}
@Override
public Object getObjectForPath(Collection<? extends Object> rootObjects, String... segments) {
RequestSupport requestSupport = new RequestSupport(rootObjects);
requestSupport.setReferenceResolver(this);
for (int i = 0; i < segments.length; i++) {
String segment = segments[i];
requestSupport.nextStep(segment, null);
}
return requestSupport.getObjects();
}
@Override
public Object getRequestQueryResult(Collection<?> target, String op, Map<String, ?> parameters) {
RequestSupport requestSupport = new RequestSupport(new BasicEList<Object>(target));
requestSupport.setReferenceResolver(this);
return requestSupport.nextStep(op, parameters);
}
}
package tdt4250.servletsupport.impl;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EObjectEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import tdt4250.servletsupport.IReferenceResolver;
public class RequestSupport {
private EList<? extends Object> objects;
public RequestSupport(Collection<?> objects) {
this.objects = new BasicEList<Object>(objects);
}
public EList<? extends Object> getObjects() {
return objects;
}
private IReferenceResolver referenceResolver;
public IReferenceResolver getReferenceResolver() {
return referenceResolver;
}
public void setReferenceResolver(IReferenceResolver referenceResolver) {
this.referenceResolver = referenceResolver;
}
//
public Object nextStep(String step, Map<String, ?> parameters) {
Integer num = null;
// try element selection
try {
num = Integer.valueOf(step);
} catch (NumberFormatException e) {
}
if (num != null) {
if (num < 0 || num >= objects.size()) {
throw new IllegalArgumentException("Cannot access element " + num + " of a list with " + objects.size() + " elements");
}
Object selected = selectElement(objects, num);
return objects = (selected != null ? ECollections.singletonEList(selected) : ECollections.emptyEList());
}
// try value selection
int pos = step.length() - 1;
String ops = "<>=";
while (pos >= 0) {
if (ops.indexOf(step.charAt(pos)) >= 0) {
break;
}
pos--;
}
if (pos >= 0) {
boolean rel1 = ops.indexOf(step.charAt(pos - 1)) < 0;
String rel = (rel1 ? step.substring(pos, pos + 1) : step.substring(pos - 1, pos + 1));
return objects = selectRelation(objects, step.substring(0, pos - (rel1 ? 0 : 1)), step.substring(pos + 1), rel);
}
EList<Object> values = null;
EObjectEList<?> eObjects = null;
int count = 0;
// try feature and operation mapping
for (Object object : objects) {
boolean hasValue = false;
Object value = null;
if (object instanceof EObject) {
EObject eObject = (EObject) object;
EStructuralFeature feature = findEStructuralFeature(eObject, step);
if (feature != null) {
hasValue = true;
value = getFeatureValue(eObject, feature);
} else {
EOperation op = findEOperation(eObject, step, parameters);
if (op != null) {
hasValue = true;
value = invokeOperation(eObject, op, parameters);
} else if (referenceResolver != null) {
value = referenceResolver.resolveReference(step, eObject);
hasValue |= (value != null);
}
}
}
if (hasValue) {
count++;
if (eObjects == null && values == null && value instanceof EObjectEList<?>) {
eObjects = ((EObjectEList<?>) value);
} else if (value != null) {
if (values == null) {
values = new BasicEList<Object>();
if (eObjects != null) {
values.addAll(eObjects);
eObjects = null;
}
}
if (value instanceof Collection<?>) {
values.addAll((Collection<?>) value);
} else {
values.add(value);
}
}
}
}
if (count == 0) {
if (Character.isUpperCase(step.charAt(0))) {
objects = selectEClass(objects, step);
if (objects.isEmpty()) {
objects = selectKey(objects, step.split(","));
}
} else {
objects = selectKey(objects, step.split(","));
}
return objects;
}
if (values != null) {
return objects = values;
}
if (eObjects != null) {
return objects = eObjects;
}
return null;
}
public static final String REQUEST_SUPPORT_ANNOTATION_SOURCE = RequestSupport.class.getName();
protected <T extends EStructuralFeature> T findEStructuralFeature(String featureName, EList<T> features) {
for (T feature : features) {
if (feature.getName().equals(featureName) && AnnotationUtil.includeTypedElement(feature, REQUEST_SUPPORT_ANNOTATION_SOURCE, true)) {
return feature;
}
}
return null;
}
protected EStructuralFeature findEStructuralFeature(EObject target, String featureName) {
return findEStructuralFeature(featureName, target.eClass().getEAllStructuralFeatures());
}
protected Object getFeatureValue(EObject target, EStructuralFeature feature) {
return target.eGet(feature);
}
protected EOperation findEOperation(EObject target, String opName, Map<String, ?> parameters) {
EOperation bestOp = null;
int bestCount = -1;
nextOp: for (EOperation op : target.eClass().getEAllOperations()) {
if (opName.equals(op.getName()) && AnnotationUtil.includeTypedElement(op, REQUEST_SUPPORT_ANNOTATION_SOURCE, true)) {
nextParam: for (EParameter param : op.getEParameters()) {
if (parameters != null && parameters.containsKey(param.getName())) {
continue nextParam;
}
continue nextOp;
}
int count = op.getEParameters().size();
if (bestOp == null || (count > bestCount)) {
bestOp = op;
bestCount = count;
}
}
}
return bestOp;
}
protected Object invokeOperation(EObject target, EOperation op, Map<String, ?> parameters) {
EList<Object> args = new BasicEList<Object>();
for (EParameter param : op.getEParameters()) {
Object paramValue = null, arg = null;
paramValue = parameters.get(param.getName());
Throwable t = null;
EClassifier type = param.getEType();
if (type.isInstance(paramValue)) {
arg = paramValue;
} else if (type instanceof EDataType) {
try {
arg = EcoreUtil.createFromString((EDataType) type, String.valueOf(paramValue));
} catch (Exception e) {
t = e;
}
} else if (getReferenceResolver() != null) {
arg = getReferenceResolver().resolveReference(String.valueOf(paramValue), target);
}
if (arg != null && type.isInstance(arg)) {
if (param.isMany()) {
arg = ECollections.singletonEList(arg);
}
args.add(arg);
} else {
throw new IllegalArgumentException("Couldn't convert " + paramValue + " to " + type, t);
}
}
try {
return target.eInvoke(op, args);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException("Couldn't invoke " + op.getName() + " on " + args);
}
}
protected Object selectElement(List<?> objects, int num) {
int size = objects.size();
if (num < 0) {
num = size + num;
}
return (num >= 0 && num < size ? objects.get(num) : null);
}
protected interface EObjectFilter {
public boolean accept(EObject eObject);
}
protected EList<EObject> selectEObjects(Collection<?> objects, EObjectFilter filter) {
EList<EObject> filtered = new BasicEList<EObject>();
for (Object object : objects) {
if (object instanceof EObject && filter.accept((EObject) object)) {
filtered.add((EObject) object);
}
}
return filtered;
}
protected EList<EObject> selectRelation(Collection<?> objects, final String featureName, final String featureValueString, final String rel) {
return selectEObjects(objects, new EObjectFilter() {
@Override
public boolean accept(EObject eObject) {
EAttribute attr = findEStructuralFeature(featureName, eObject.eClass().getEAllAttributes());
if (attr != null) {
Object featureValue1 = eObject.eGet(attr);
Object featureValue2 = EcoreUtil.createFromString(attr.getEAttributeType(), featureValueString);
if (rel == null || rel.equals("=")) {
if (featureValue1 == featureValue2 || (featureValue1 != null && featureValue1.equals(featureValue2))) {
return true;
}
} else if (featureValue1 instanceof Comparable<?>) {
try {
int comp = ((Comparable) featureValue1).compareTo(featureValue2);
return (rel.equals("==") && comp == 0)
|| (rel.equals(">") && comp > 0)
|| ((rel.equals(">=") || rel.equals("=>")) && comp >= 0)
|| ((rel.equals("<=") || rel.equals("=<")) && comp <= 0)
|| (rel.equals("<") && comp < 0)
|| ((rel.equals("<>") || rel.equals("><")) && comp != 0);
} catch (RuntimeException e) {
}
}
}
return false;
}
});
}
protected EList<EObject> selectKey(Collection<?> objects, final String[] keyValueStrings) {
return selectEObjects(objects, new EObjectFilter() {
@Override
public boolean accept(EObject eObject) {
EStructuralFeature containingFeature = eObject.eContainingFeature();
if (containingFeature instanceof EReference) {
EList<EAttribute> keyAttributes = ((EReference) containingFeature).getEKeys();
if (keyAttributes.size() == keyValueStrings.length) {
for (int i = 0; i < keyAttributes.size(); i++) {
EAttribute attr = keyAttributes.get(i);
Object value = EcoreUtil.createFromString(attr.getEAttributeType(), keyValueStrings[i]);
if (value == null || (! value.equals(eObject.eGet(attr)))) {
return false;
}
}
return true;
}
}
return false;
}
});
}
private EList<EObject> selectEClass(Collection<?> objects, final String step) {
return selectEObjects(objects, new EObjectFilter() {
private boolean accept(EClass eClass) {
return eClass.getName().equals(step);
}
@Override
public boolean accept(EObject eObject) {
if (accept(eObject.eClass())) {
return true;
}
for (EClass eClass : eObject.eClass().getEAllSuperTypes()) {
if (accept(eClass)) {
return true;
}
}
return false;
}
});
}
}
package tdt4250.servletsupport.impl;
import java.io.IOException;
import java.util.Collection;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import tdt4250.servletsupport.IResourceProvider;
public class ResourceProvider implements IResourceProvider {
private URI uri;
private Resource resource;
public ResourceProvider() {
}
public ResourceProvider(URI uri) {
this.uri = uri;
}
public void setUri(URI uri) {
this.uri = uri;
}
public ResourceProvider(Resource resource) {
this.resource = resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public Resource getResource() {
if (resource == null && uri != null) {
ResourceSet resourceSet = new ResourceSetImpl();
configureResourceSet(resourceSet);
Resource resource = resourceSet.createResource(uri);
try {
resource.load(null);
this.resource = resource;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return this.resource;
}
protected void configureResourceSet(ResourceSet resourceSet) {
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xmi", new XMIResourceFactoryImpl());
}
private String name;
private EClass rootObjectClass;
@Override
public String getName() {
if (name != null) {
return name;
}
if (resource != null) {
return defaultName(resource.getURI());
} else if (uri != null) {
return defaultName(uri);
}
return null;
}
public void setRootObjectClass(EClass rootObjectClass) {
this.rootObjectClass = rootObjectClass;
}
public static String defaultName(URI uri) {
return uri.trimFileExtension().lastSegment().replace('.', '/');
}
public void setName(String name) {
this.name = name;
}
@Override
public Collection<? extends Object> getRootObjects() {
Collection<? extends Object> objects = getResource().getContents();
if (rootObjectClass != null) {
objects = EcoreUtil.getObjectsByType(objects, rootObjectClass);
}
return objects;
}
}
@org.osgi.annotation.versioning.Version("1.0.0")
@org.osgi.annotation.bundle.Export
package tdt4250.servletsupport.impl;
# To make sure the folder exists in git
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
<classpathentry kind="src" output="bin" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment