Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoberto E. Escobar2014-08-02 00:26:46 +0000
committerRoberto E. Escobar2014-09-29 22:55:02 +0000
commitad47a2aa52b0ef52d11a325dd1a859ca515b8c29 (patch)
treebf8afb1abf427c2bb9ed1d75301e49b7ec1df240
parentf2ec51986b919ecb904b9189da15eeb3acdb6272 (diff)
downloadorg.eclipse.osee-ad47a2aa52b0ef52d11a325dd1a859ca515b8c29.tar.gz
org.eclipse.osee-ad47a2aa52b0ef52d11a325dd1a859ca515b8c29.tar.xz
org.eclipse.osee-ad47a2aa52b0ef52d11a325dd1a859ca515b8c29.zip
feature[ats_ATS96758]: Enable template engine as MVC resolver
Allow templates to be contributed through the bundle's MANIFEST file by using Osee-Template header. Contribute PageFactory as a JAX-RS MVC ViewResolver. Change-Id: Ib4b37bcadfb47164e6d97920a71b173b7c93c6b8
-rw-r--r--plugins/org.eclipse.osee.template.engine/META-INF/MANIFEST.MF11
-rw-r--r--plugins/org.eclipse.osee.template.engine/OSGI-INF/jaxrs.mvc.page.factory.resolver.xml8
-rw-r--r--plugins/org.eclipse.osee.template.engine/OSGI-INF/template.registry.xml8
-rw-r--r--plugins/org.eclipse.osee.template.engine/build.properties3
-rw-r--r--plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/PageFactoryViewResolver.java63
-rw-r--r--plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/TemplateRegistry.java26
-rw-r--r--plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/TemplateRegistryImpl.java366
7 files changed, 482 insertions, 3 deletions
diff --git a/plugins/org.eclipse.osee.template.engine/META-INF/MANIFEST.MF b/plugins/org.eclipse.osee.template.engine/META-INF/MANIFEST.MF
index adc81df57a6..742e8939996 100644
--- a/plugins/org.eclipse.osee.template.engine/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.osee.template.engine/META-INF/MANIFEST.MF
@@ -4,7 +4,14 @@ Bundle-Name: OSEE Template Engine (Incubation)
Bundle-SymbolicName: org.eclipse.osee.template.engine
Bundle-Version: 0.19.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
-Import-Package: org.eclipse.osee.framework.jdk.core.type,
- org.eclipse.osee.framework.jdk.core.util
+Import-Package: javax.ws.rs.core;version="2.0.0",
+ org.eclipse.osee.framework.core.util,
+ org.eclipse.osee.framework.jdk.core.type,
+ org.eclipse.osee.framework.jdk.core.util,
+ org.eclipse.osee.jaxrs.mvc,
+ org.eclipse.osee.logger,
+ org.eclipse.osgi.util,
+ org.osgi.framework
+Service-Component: OSGI-INF/*.xml
Bundle-Vendor: Eclipse Open System Engineering Environment
Export-Package: org.eclipse.osee.template.engine
diff --git a/plugins/org.eclipse.osee.template.engine/OSGI-INF/jaxrs.mvc.page.factory.resolver.xml b/plugins/org.eclipse.osee.template.engine/OSGI-INF/jaxrs.mvc.page.factory.resolver.xml
new file mode 100644
index 00000000000..6d9702b2bd7
--- /dev/null
+++ b/plugins/org.eclipse.osee.template.engine/OSGI-INF/jaxrs.mvc.page.factory.resolver.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" >
+ <implementation class="org.eclipse.osee.template.engine.internal.PageFactoryViewResolver" />
+ <service>
+ <provide interface="org.eclipse.osee.jaxrs.mvc.ViewResolver"/>
+ </service>
+ <reference bind="setTemplateRegistry" cardinality="1..1" interface="org.eclipse.osee.template.engine.internal.TemplateRegistry" name="TemplateRegistry" policy="static"/>
+</scr:component>
diff --git a/plugins/org.eclipse.osee.template.engine/OSGI-INF/template.registry.xml b/plugins/org.eclipse.osee.template.engine/OSGI-INF/template.registry.xml
new file mode 100644
index 00000000000..1e5b8d141c4
--- /dev/null
+++ b/plugins/org.eclipse.osee.template.engine/OSGI-INF/template.registry.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="start" deactivate="stop">
+ <implementation class="org.eclipse.osee.template.engine.internal.TemplateRegistryImpl" />
+ <service>
+ <provide interface="org.eclipse.osee.template.engine.internal.TemplateRegistry"/>
+ </service>
+ <reference bind="setLogger" cardinality="1..1" interface="org.eclipse.osee.logger.Log" name="Log" policy="static"/>
+</scr:component>
diff --git a/plugins/org.eclipse.osee.template.engine/build.properties b/plugins/org.eclipse.osee.template.engine/build.properties
index 40db5d80b59..b5f22786662 100644
--- a/plugins/org.eclipse.osee.template.engine/build.properties
+++ b/plugins/org.eclipse.osee.template.engine/build.properties
@@ -2,4 +2,5 @@ source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
- src/org/eclipse/osee/template/engine/html/
+ src/org/eclipse/osee/template/engine/html/,\
+ OSGI-INF/
diff --git a/plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/PageFactoryViewResolver.java b/plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/PageFactoryViewResolver.java
new file mode 100644
index 00000000000..70c01cf2d31
--- /dev/null
+++ b/plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/PageFactoryViewResolver.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Boeing.
+ * 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:
+ * Boeing - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osee.template.engine.internal;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
+import java.util.Map.Entry;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import org.eclipse.osee.framework.jdk.core.type.ResourceToken;
+import org.eclipse.osee.framework.jdk.core.type.ViewModel;
+import org.eclipse.osee.jaxrs.mvc.AbstractViewResolver;
+import org.eclipse.osee.template.engine.AppendableRule;
+import org.eclipse.osee.template.engine.PageCreator;
+import org.eclipse.osee.template.engine.PageFactory;
+
+/**
+ * @author Roberto E. Escobar
+ */
+public class PageFactoryViewResolver extends AbstractViewResolver<ResourceToken> {
+
+ private TemplateRegistryImpl registry;
+
+ public void setTemplateRegistry(TemplateRegistryImpl registry) {
+ this.registry = registry;
+ }
+
+ @Override
+ public ResourceToken resolve(String viewId, MediaType mediaType) {
+ return registry.resolveTemplate(viewId, mediaType);
+ }
+
+ @Override
+ public void write(ViewModel model, ResourceToken view, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream output, Charset encoding) throws IOException {
+ PageCreator pageCreator = PageFactory.newPageCreator(registry.getResourceRegistry());
+ for (Entry<String, Object> entry : model.asMap().entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (value instanceof AppendableRule) {
+ pageCreator.addSubstitution((AppendableRule<?>) value);
+ } else {
+ pageCreator.addKeyValuePair(key, String.valueOf(value));
+ }
+ }
+ OutputStreamWriter writer = new OutputStreamWriter(output, encoding);
+ try {
+ pageCreator.realizePage(view, writer);
+ } finally {
+ writer.flush();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/TemplateRegistry.java b/plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/TemplateRegistry.java
new file mode 100644
index 00000000000..2a56fd3a987
--- /dev/null
+++ b/plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/TemplateRegistry.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Boeing.
+ * 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:
+ * Boeing - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osee.template.engine.internal;
+
+import javax.ws.rs.core.MediaType;
+import org.eclipse.osee.framework.jdk.core.type.IResourceRegistry;
+import org.eclipse.osee.framework.jdk.core.type.ResourceToken;
+
+/**
+ * @author Roberto E. Escobar
+ */
+public interface TemplateRegistry {
+
+ ResourceToken resolveTemplate(String viewId, MediaType mediaType);
+
+ IResourceRegistry getResourceRegistry();
+
+} \ No newline at end of file
diff --git a/plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/TemplateRegistryImpl.java b/plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/TemplateRegistryImpl.java
new file mode 100644
index 00000000000..5b03c5df2a8
--- /dev/null
+++ b/plugins/org.eclipse.osee.template.engine/src/org/eclipse/osee/template/engine/internal/TemplateRegistryImpl.java
@@ -0,0 +1,366 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Boeing.
+ * 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:
+ * Boeing - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osee.template.engine.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.ws.rs.core.MediaType;
+import org.eclipse.osee.framework.core.util.HexUtil;
+import org.eclipse.osee.framework.jdk.core.type.IResourceRegistry;
+import org.eclipse.osee.framework.jdk.core.type.ResourceToken;
+import org.eclipse.osee.framework.jdk.core.util.ChecksumUtil;
+import org.eclipse.osee.framework.jdk.core.util.Strings;
+import org.eclipse.osee.logger.Log;
+import org.eclipse.osee.template.engine.OseeTemplateTokens;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+
+/**
+ * @author Roberto E. Escobar
+ */
+public class TemplateRegistryImpl implements TemplateRegistry, IResourceRegistry, BundleListener {
+
+ private static final String OSEE_TEMPLATE_HDR = "Osee-Template";
+ private static final String OSEE_TEMPLATE_HDR__UUID_ATTRIBUTE = "uuid";
+
+ private ConcurrentHashMap<String, TemplateResources> templates;
+ private ConcurrentHashMap<Long, ResourceToken> tokenByUuid;
+ private ConcurrentHashMap<String, ResourceToken> tokenByName;
+
+ private Log logger;
+
+ public void setLogger(Log logger) {
+ this.logger = logger;
+ }
+
+ public void start(BundleContext context) {
+ templates = new ConcurrentHashMap<String, TemplateResources>();
+ tokenByName = new ConcurrentHashMap<String, ResourceToken>();
+ tokenByUuid = new ConcurrentHashMap<Long, ResourceToken>();
+
+ OseeTemplateTokens.register(this);
+
+ context.addBundleListener(this);
+
+ // Add bundles that have already started
+ Bundle[] bundles = context.getBundles();
+ if (bundles != null) {
+ for (Bundle bundle : bundles) {
+ int state = bundle.getState();
+ processBundle(bundle, state);
+ }
+ }
+ }
+
+ public void stop(BundleContext context) {
+ context.removeBundleListener(this);
+
+ templates.clear();
+ tokenByName.clear();
+ tokenByUuid.clear();
+ }
+
+ private void cache(ResourceToken token) {
+ tokenByUuid.put(token.getGuid(), token);
+ ResourceToken oldToken = tokenByName.put(token.getName(), token);
+ if (oldToken != null) {
+ logger.error("Template conflict detected between - [%s] and [%s]", oldToken, token);
+ }
+ }
+
+ private void decache(ResourceToken token) {
+ tokenByUuid.remove(token.getGuid());
+ tokenByName.remove(token.getName());
+ }
+
+ private String getTemplateHeader(Bundle bundle) {
+ return bundle.getHeaders().get(OSEE_TEMPLATE_HDR);
+ }
+
+ private boolean hasTemplateHeader(Bundle bundle) {
+ String headerValue = getTemplateHeader(bundle);
+ return Strings.isValid(headerValue);
+ }
+
+ @Override
+ public ResourceToken resolveTemplate(String viewId, MediaType mediaType) {
+ ResourceToken resourceToken = null;
+ if (Strings.isNumeric(viewId)) {
+ Long uuid = Long.parseLong(viewId);
+ resourceToken = getResourceToken(uuid);
+ } else {
+ String name = viewId;
+ int index = name.lastIndexOf("/");
+ if (index > -1) {
+ name = name.substring(index + 1);
+ }
+ resourceToken = tokenByName.get(name);
+ }
+ return resourceToken;
+ }
+
+ @Override
+ public IResourceRegistry getResourceRegistry() {
+ return this;
+ }
+
+ @Override
+ public ResourceToken registerResource(Long universalId, ResourceToken token) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void registerAll(Iterable<ResourceToken> tokens) {
+ for (ResourceToken token : tokens) {
+ cache(token);
+ }
+ }
+
+ @Override
+ public ResourceToken getResourceToken(Long uuid) {
+ return tokenByUuid.get(uuid);
+ }
+
+ @Override
+ public InputStream getResource(Long uuid) {
+ ResourceToken token = getResourceToken(uuid);
+ InputStream toReturn = null;
+ if (token != null) {
+ toReturn = token.getInputStream();
+ } else {
+ logger.error("Unable to find template-resource [%s]", uuid);
+ }
+ return toReturn;
+ }
+
+ @Override
+ public void bundleChanged(BundleEvent event) {
+ Bundle bundle = event.getBundle();
+ processBundle(bundle, event.getType());
+ }
+
+ private void processBundle(Bundle bundle, int state) {
+ boolean isActive = true;
+ boolean isStopping = false;
+ if (state == Bundle.ACTIVE //
+ || state == Bundle.STARTING //
+ || state == Bundle.INSTALLED //
+ || state == Bundle.RESOLVED) {
+ isActive = true;
+ } else if (state == Bundle.STOPPING) {
+ isStopping = true;
+ }
+
+ if (isActive && hasTemplateHeader(bundle)) {
+ addBundle(bundle);
+ } else if (isStopping && hasTemplateHeader(bundle)) {
+ removeBundle(bundle);
+ }
+ }
+
+ private void removeBundle(Bundle bundle) {
+ String bundleName = bundle.getSymbolicName();
+ TemplateResources removed = templates.remove(bundleName);
+ if (removed != null) {
+ Iterable<ResourceToken> tokens = removed.getTokens();
+ for (ResourceToken token : tokens) {
+ decache(token);
+ }
+ }
+ }
+
+ private void addBundle(Bundle bundle) {
+ String bundleName = bundle.getSymbolicName();
+ String headerValue = getTemplateHeader(bundle);
+
+ ManifestElement[] elements = null;
+ try {
+ elements = ManifestElement.parseHeader(OSEE_TEMPLATE_HDR, headerValue);
+ } catch (BundleException ex) {
+ logger.error(ex, "Error parsing manifest header [%s] for bundle [%s]", OSEE_TEMPLATE_HDR, bundleName);
+ }
+
+ if (elements != null && elements.length > 0) {
+ TemplateResources tokens = new TemplateResources();
+ for (ManifestElement element : elements) {
+ String resource = element.getValue();
+ String uuidAttribute = element.getAttribute(OSEE_TEMPLATE_HDR__UUID_ATTRIBUTE);
+ List<URL> resourceUrls = findUrls(bundle, headerValue, false, resource);
+
+ if (resourceUrls != null && !resourceUrls.isEmpty()) {
+ if (Strings.isValid(uuidAttribute)) {
+ if (isValidUuid(uuidAttribute)) {
+ URL url = resourceUrls.iterator().next();
+ Long uuid = HexUtil.toLong(uuidAttribute);
+ addEntry(bundleName, headerValue, tokens, url, uuid);
+ } else {
+ logger.error("Invalid uuidAttribute [%s] for manifest element [%s] in bundle [%s]", uuidAttribute,
+ element, bundleName);
+ }
+ } else {
+ for (URL url : resourceUrls) {
+ addEntry(bundleName, headerValue, tokens, url, null);
+ }
+ }
+ }
+ }
+ if (!tokens.isEmpty()) {
+ templates.put(bundleName, tokens);
+ }
+ }
+ }
+
+ private boolean isValidUuid(String value) {
+ return HexUtil.isHexString(value);
+ }
+
+ private void addEntry(String bundleName, String headerValue, TemplateResources tokens, URL url, Long uuidSpecified) {
+ try {
+ String name = urlAsName(url);
+ Long uuid = uuidSpecified != null ? uuidSpecified : nameToUuid(name);
+ ResourceToken token = newToken(bundleName, uuid, name, url);
+ boolean wasAdded = tokens.addToken(token);
+ if (!wasAdded) {
+ logger.error("Invalid template uuid conflicts with previous definition - bundle [%s] header [%s]",
+ bundleName, headerValue);
+ }
+ cache(token);
+ } catch (Exception ex) {
+ logger.error(ex, "Invalid uuidAttribute for bundle [%s] with header [%s]", bundleName, headerValue);
+ }
+ }
+
+ private Long nameToUuid(String name) throws Exception {
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(name.getBytes("UTF-8"));
+ byte[] checksum = ChecksumUtil.createChecksum(inputStream, ChecksumUtil.SHA);
+ long value = 0;
+ for (int i = 0; i < 8; i++) {
+ value += (checksum[i] & 0xffL) << (8 * i);
+ }
+ return value;
+ }
+
+ private String urlAsName(URL url) {
+ String name = url.toString();
+ int index = name.lastIndexOf('/');
+ if (index > -1) {
+ name = name.substring(index + 1);
+ }
+ return name;
+ }
+
+ private List<URL> findUrls(Bundle bundle, String headerValue, boolean recurse, String... resources) {
+ List<URL> resourceUrls = new ArrayList<URL>();
+ for (String resource : resources) {
+ int index = resource.lastIndexOf('/');
+ String path = index != -1 ? resource.substring(0, index) : "/";
+ String resourceName = index != -1 ? resource.substring(index + 1) : resource;
+ Enumeration<URL> urls = bundle.findEntries(path, resourceName, recurse);
+ if (urls != null && urls.hasMoreElements()) {
+ while (urls.hasMoreElements()) {
+ URL url = urls.nextElement();
+ resourceUrls.add(url);
+ }
+ } else {
+ logger.error("Unable to find template-resource[%s] for bundle [%s]. The component header value is [%s]",
+ resource, bundle.getSymbolicName(), headerValue);
+ }
+ }
+ return resourceUrls;
+ }
+
+ private static ResourceToken newToken(String bundleName, Long uuid, String name, URL url) {
+ return new TemplateToken(bundleName, uuid, name, url);
+ }
+
+ private static class TemplateResources {
+ private final Set<ResourceToken> tokens = new HashSet<ResourceToken>();
+
+ public boolean addToken(ResourceToken token) {
+ return tokens.add(token);
+ }
+
+ public Iterable<ResourceToken> getTokens() {
+ return tokens;
+ }
+
+ public boolean isEmpty() {
+ return tokens.isEmpty();
+ }
+ }
+
+ private static class TemplateToken extends ResourceToken {
+ private final String bundleName;
+ private final Long uuid;
+ private final String name;
+ private final URL url;
+
+ public TemplateToken(String bundleName, Long uuid, String name, URL url) {
+ super(uuid, name);
+ this.bundleName = bundleName;
+ this.uuid = uuid;
+ this.name = name;
+ this.url = url;
+ }
+
+ @Override
+ public URL getUrl() {
+ return url;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((url == null) ? 0 : url.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ResourceToken other = (ResourceToken) obj;
+ if (getUrl() == null) {
+ if (other.getUrl() != null) {
+ return false;
+ }
+ } else if (!getUrl().equals(other.getUrl())) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s::%s::%s - [%s]", bundleName, uuid, name, url);
+ }
+ }
+
+} \ No newline at end of file

Back to the top