You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by ss...@apache.org on 2013/02/22 16:21:37 UTC
[5/37] MARMOTTA-105: renamed packages in marmotta-core
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/startup/LMFStartupService.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/startup/LMFStartupService.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/startup/LMFStartupService.java
new file mode 100644
index 0000000..f9088bb
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/startup/LMFStartupService.java
@@ -0,0 +1,244 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.startup;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Event;
+import javax.enterprise.inject.Any;
+import javax.inject.Inject;
+import javax.servlet.ServletContext;
+
+import org.apache.marmotta.platform.core.api.config.ConfigurationService;
+import org.apache.marmotta.platform.core.api.modules.ModuleService;
+import org.apache.marmotta.platform.core.api.triplestore.SesameService;
+import org.apache.marmotta.platform.core.api.ui.LMFSystrayLink;
+import org.apache.marmotta.platform.core.api.user.UserService;
+import org.apache.marmotta.platform.core.events.SesameStartupEvent;
+import org.apache.marmotta.platform.core.events.SystemStartupEvent;
+import org.apache.marmotta.platform.core.model.module.ModuleConfiguration;
+import org.apache.marmotta.platform.core.util.KiWiContext;
+
+import org.apache.commons.configuration.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This service unifies the different steps in the LMF startup. It offers several methods
+ * for triggering the different startup sequences and can be used e.g. by web applications or
+ * embedded applications to initiate LMF startup. Note that the LMF Startup requires a running
+ * CDI/Weld environment before being used.
+ * <p/>
+ * Author: Sebastian Schaffert
+ */
+@ApplicationScoped
+public class LMFStartupService {
+
+ private static final String DEFAULT_KIWI_VERSION = "undefined";
+
+ private Logger log = LoggerFactory.getLogger(LMFStartupService.class);
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ @Inject
+ private ModuleService moduleService;
+
+ @Inject @Any
+ private Event<SystemStartupEvent> startupEvent;
+
+ @Inject @Any
+ private Event<SesameStartupEvent> sesameEvent;
+
+ private boolean configurationStarted = false;
+
+ private boolean hostStarted = false;
+
+ private ReentrantLock lock;
+
+ @PostConstruct
+ public void initialise() {
+ lock = new ReentrantLock();
+ }
+
+ /**
+ * Startup the LMF Configuration. This method ensures that the LMF home directory is created and the
+ * ConfigurationService is properly initialised. It must be called first in the startup sequence.
+ * The parameters lmfHome and configurationOverride can be used to override the default settings
+ * of the LMF.
+ *
+ * @param lmfHome home directory of the LMF instance (may be null, in which case the default will be used)
+ * @param configurationOverride configuration options that should override the default values from default-config.properties (may be null)
+ * @param context the servlet context the LMF is running in (may be null)
+ */
+ public void startupConfiguration(String lmfHome, Configuration configurationOverride, ServletContext context) {
+ lock.lock();
+
+ //to set config version number
+ String versionNumber = DEFAULT_KIWI_VERSION;
+
+ try {
+ if(configurationStarted) {
+ log.warn("LMF Startup: configuration already started; ignoring second request");
+ return;
+ }
+
+ ModuleConfiguration coreConfiguration = moduleService.getModuleConfiguration(this.getClass());
+
+ if(coreConfiguration.hasBuildInfo()) {
+ log.info("LMF Core Version {} starting up ... ", coreConfiguration.getModuleVersion());
+ log.info("Build Information:");
+ log.info(" - Build User: {}", coreConfiguration.getBuildUser());
+ log.info(" - Build Host: {}", coreConfiguration.getBuildHost());
+ log.info(" - Build Time: {}", coreConfiguration.getBuildTimestamp());
+ log.info(" - Build OS: {}", coreConfiguration.getBuildOS());
+ log.info(" - Revision: {}:{}", coreConfiguration.getBuildRevisionNumber(), coreConfiguration.getBuildRevisionHash());
+ versionNumber = coreConfiguration.getModuleVersion();
+ } else {
+ log.info("LMF Core (Development Version) starting up ... ");
+ }
+
+ String kiwiHome = lmfHome;
+
+ if(kiwiHome != null) {
+
+ } else {
+ kiwiHome = System.getProperty("kiwi.home");
+
+ if(kiwiHome != null) {
+ log.info("Configured working directory {} from system property kiwi.home",kiwiHome);
+ } else {
+ kiwiHome = System.getenv("LMF_HOME");
+ if(kiwiHome != null) {
+ log.info("Configured working directory {} from environment variable LMF_HOME",kiwiHome);
+ } else {
+ kiwiHome = System.getenv("KIWI_HOME");
+ if(kiwiHome != null) {
+ log.info("Configured working directory {} from environment variable KIWI_HOME",kiwiHome);
+ } else if (context != null) {
+ kiwiHome = context.getInitParameter("kiwi.home");
+ if(kiwiHome != null) {
+ log.info("Configured working directory {} from servlet context parameter kiwi.home",kiwiHome);
+ }
+ } else {
+ log.error("could not determine LMF home directory, please set the environment variable LMF_HOME");
+ }
+ }
+ }
+ }
+
+ if(kiwiHome != null) {
+ configurationService.setLMFHome(kiwiHome);
+ }
+
+ if(context != null) {
+ configurationService.setServletContext(context);
+ }
+
+ configurationService.initialize(kiwiHome,configurationOverride);
+
+
+ configurationService.setConfiguration("kiwi.version",versionNumber);
+
+ if(context != null) {
+ configurationService.setConfiguration("kiwi.path",context.getContextPath());
+
+ // register the systray links provided by the different components
+ Map<String, String> demoLinks = new HashMap<String, String>();
+ Map<String, String> adminLinks = new HashMap<String, String>();
+
+ for(LMFSystrayLink link : KiWiContext.getInstances(LMFSystrayLink.class)) {
+ if(link.getSection() == LMFSystrayLink.Section.DEMO) {
+ demoLinks.put(link.getLabel(), link.getLink());
+ } else if(link.getSection() == LMFSystrayLink.Section.ADMIN) {
+ adminLinks.put(link.getLabel(), link.getLink());
+ }
+ }
+ context.setAttribute("systray.admin", adminLinks);
+ context.setAttribute("systray.demo", demoLinks);
+ }
+
+ configurationStarted = true;
+ } finally {
+ lock.unlock();
+ }
+
+ }
+
+
+ /**
+ * Start up the LMF server environment. This method ensures that the base URL for the host (used by the
+ * web interface) and the context (used for creating local Linked Data URIs) is properly set and thus
+ * the services depending on this configuration can start up. This method must be called in the second
+ * phase of LMF startup, i.e. when the configuration service is already configured.
+ * <p/>
+ * The method expects a host URL and a context URL to be given. In case the context URL is not given,
+ * it will be the same as the host URL.
+ *
+ * @param hostUrl the URL of the host, used as based URL for building the LMF web interface
+ * @param contextUrl the base URL used to construct Linked Data resources
+ */
+ public void startupHost(String hostUrl, String contextUrl) {
+ lock.lock();
+
+ try {
+ if(hostStarted) {
+ log.warn("LMF Startup: host already started; ignoring subsequent startup requests");
+ return;
+ }
+
+ // check whether this is a first-time initialization
+ boolean isSetup = configurationService.getBooleanConfiguration("kiwi.setup.host");
+
+ // carry out initializations that need the server URI to be set properly
+ if(!isSetup) {
+ log.info("SETUP: Setting up initial host and resource configuration ({}) ...", hostUrl);
+
+ configurationService.setConfiguration("kiwi.context", contextUrl);
+ configurationService.setConfiguration("kiwi.host", hostUrl);
+
+ configurationService.setConfiguration("kiwi.setup.host", true);
+ }
+
+ // trigger startup of the sesame service once the hostname is ready (we need the correct URIs for
+ // default, cache and inferred context)
+ SesameService sesameService = KiWiContext.getInstance(SesameService.class);
+ sesameService.initialise();
+ sesameEvent.fire(new SesameStartupEvent());
+
+ // trigger startup of the user service once the sesame service is ready
+ UserService userService = KiWiContext.getInstance(UserService.class);
+
+ userService.createDefaultUsers();
+
+ hostStarted = true;
+
+ startupEvent.fire(new SystemStartupEvent());
+ } finally {
+ lock.unlock();
+ }
+
+ }
+
+ public void shutdown() {
+ log.info("LMF Core shutting down ...");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/CdiUtils.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/CdiUtils.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/CdiUtils.java
new file mode 100644
index 0000000..eced943
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/CdiUtils.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util;
+
+import javax.enterprise.inject.Instance;
+import javax.inject.Named;
+import java.lang.annotation.Annotation;
+
+public abstract class CdiUtils {
+
+ public static <T> Instance<T> selectNamed(Instance<T> instance, String name) {
+ if (name != null)
+ return instance.select(createNamedLiteral(name));
+ return instance;
+ }
+
+ private static Annotation createNamedLiteral(final String name) {
+ return new Named() {
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return Named.class;
+ }
+
+ @Override
+ public String value() {
+ return name;
+ }
+ };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/DateUtil.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/DateUtil.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/DateUtil.java
new file mode 100644
index 0000000..8948d7c
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/DateUtil.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class DateUtil {
+
+ public static final String XSD_DATETIME = "yyyy-MM-dd'T'HH:mm:ssZ";
+
+ public static Date parse(String xmlDateTime) throws ParseException {
+ if ( xmlDateTime.length() != 25 ) {
+ throw new ParseException("Date not in expected xsd:datetime format", 0);
+ }
+ StringBuilder sb = new StringBuilder(xmlDateTime);
+ sb.deleteCharAt(22);
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(XSD_DATETIME);
+ return simpleDateFormat.parse(sb.toString());
+ }
+
+ public static String serializeXsdDateTime(Date date) {
+ return serializeXsdDateTime(date, TimeZone.getDefault());
+ }
+
+ public static String serializeXsdDateTime(Date date, TimeZone timezone) {
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(XSD_DATETIME);
+ simpleDateFormat.setTimeZone(timezone);
+ String s = simpleDateFormat.format(date);
+ StringBuilder sb = new StringBuilder(s);
+ sb.insert(22, ':');
+ return sb.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/FallbackConfiguration.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/FallbackConfiguration.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/FallbackConfiguration.java
new file mode 100644
index 0000000..1ac2de3
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/FallbackConfiguration.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.Configuration;
+
+import java.util.Collection;
+import java.util.List;
+
+public class FallbackConfiguration extends CompositeConfiguration {
+
+ public FallbackConfiguration() {
+ super();
+ }
+
+ public FallbackConfiguration(Collection<? extends Configuration> configurations) {
+ super(configurations);
+ }
+
+ public FallbackConfiguration(Configuration inMemoryConfiguration, Collection<? extends Configuration> configurations) {
+ super(inMemoryConfiguration, configurations);
+ }
+
+ public FallbackConfiguration(Configuration inMemoryConfiguration) {
+ super(inMemoryConfiguration);
+ }
+
+ @Override
+ public List<Object> getList(String key, List<Object> defaultValue) {
+ final Configuration mem = getInMemoryConfiguration();
+ if (mem.containsKey(key))
+ return mem.getList(key, defaultValue);
+ else
+ return super.getList(key, defaultValue);
+ }
+
+ @Override
+ public List<Object> getList(String key) {
+ final Configuration mem = getInMemoryConfiguration();
+ if (mem.containsKey(key))
+ return mem.getList(key);
+ else
+ return super.getList(key);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/JerseyUtils.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/JerseyUtils.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/JerseyUtils.java
new file mode 100644
index 0000000..0c3e346
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/JerseyUtils.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util;
+
+import javax.ws.rs.Path;
+
+/**
+ * Utilities for working with Jersey
+ *
+ * @author Sergio Fernández
+ *
+ */
+public class JerseyUtils {
+
+ /**
+ * Get the resource's path
+ *
+ * @param resource target resource
+ * @return path
+ */
+ public static String getResourcePath(Object resource) {
+ try {
+ return (String) ReflectionUtils.getAnnotationValue(resource, Path.class, "value");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/KiWiContext.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/KiWiContext.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/KiWiContext.java
new file mode 100644
index 0000000..7fc4907
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/KiWiContext.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.naming.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * KiWi (LMF) Content Util
+ *
+ * @author Sebastian Schaffert
+ */
+public class KiWiContext {
+ private static BeanManager beanManager;
+
+ private static final List<String> beanManagerLocations;
+
+ private static Logger log = LoggerFactory.getLogger(KiWiContext.class);
+
+ static {
+ beanManagerLocations = new ArrayList<String>();
+ beanManagerLocations.add("java:comp/env/BeanManager");
+ beanManagerLocations.add("java:comp/BeanManager");
+ beanManagerLocations.add("java:app/BeanManager");
+ }
+
+
+ protected static BeanManager getBeanManager() {
+ if (beanManager == null)
+ {
+ beanManager = lookupBeanManager();
+ }
+ return beanManager;
+ }
+
+
+ private static BeanManager lookupBeanManager() {
+ for (String location : beanManagerLocations) {
+ try {
+ return (BeanManager) new InitialContext().lookup(location);
+ } catch (NameNotFoundException e) {
+ // do nothing
+ } catch (NamingException e) {
+ log.error(
+ "naming exception for path {}; this probably means that JNDI is not set up properly (see e.g. http://code.google.com/p/lmf/wiki/InstallationSetup#Specific_Settings_for_Tomcat )",
+ location, e);
+ }
+ }
+ // in case no JNDI resource for the bean manager is found, display the JNDI context for debugging
+ try {
+ log.info("Could not find BeanManager in {}; list of JNDI context follows", beanManagerLocations);
+ showJndiContext(new InitialContext(),"java:", "");
+ } catch (NamingException e) {
+ log.error("error listing JNDI context",e);
+ }
+ throw new IllegalArgumentException("Could not find BeanManager in " + beanManagerLocations);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> T getInstance(Class<T> type) {
+
+ BeanManager beanManager = getBeanManager();
+ Bean<T> bean = (Bean<T>) beanManager.getBeans(type).iterator().next();
+ CreationalContext<T> context = beanManager.createCreationalContext(bean);
+ return (T) beanManager.getReference(bean, type, context);
+ }
+
+ /**
+ * Return all injectable instances of the given type.
+ * @param type
+ * @param <T>
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> List<T> getInstances(Class<T> type) {
+
+ BeanManager beanManager = getBeanManager();
+
+ List<T> result = new ArrayList<T>();
+ for(Bean<?> bean : beanManager.getBeans(type)) {
+ CreationalContext<T> context = beanManager.createCreationalContext((Bean<T>)bean);
+ result.add((T) beanManager.getReference(bean, type, context));
+ }
+
+ return result;
+ }
+
+
+ public static void showJndiContext(Context ctx, String name, String path) {
+ try {
+ NamingEnumeration<NameClassPair> bindings = ctx.list(name);
+ while(bindings.hasMoreElements()) {
+ NameClassPair pair = bindings.nextElement();
+ log.info("Found JNDI resource: {}{} = {}", path,pair.getName(),pair.getClassName());
+ if(pair.getClassName().endsWith("NamingContext")) {
+ showJndiContext((Context)ctx.lookup(name+ (name.length()>5?"/":"")+pair.getName()),"",path+"--");
+ }
+ }
+ }catch ( NamingException ex ) {}
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/KiWiIO.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/KiWiIO.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/KiWiIO.java
new file mode 100644
index 0000000..f3c52ca
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/KiWiIO.java
@@ -0,0 +1,114 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Add file description here!
+ * <p/>
+ * Author: Sebastian Schaffert
+ */
+public class KiWiIO {
+
+ /**
+ * Deletes all the files within a directory. Does not delete the directory
+ * itself.
+ *
+ * <p>
+ * If the file argument is a symbolic link or there is a symbolic link in
+ * the path leading to the directory, this method will do nothing. Symbolic
+ * links within the directory are not followed.
+ *
+ * @param directory
+ * the directory to delete the contents of
+ * @throws IllegalArgumentException
+ * if the argument is not a directory
+ * @throws IOException
+ * if an I/O error occurs
+ * @see #deleteRecursively
+ */
+ public static void deleteDirectoryContents(File directory)
+ throws IOException {
+ Preconditions.checkArgument(directory.isDirectory(),
+ "Not a directory: %s", directory);
+ File[] files = directory.listFiles();
+ if (files == null) {
+ throw new IOException("Error listing files for " + directory);
+ }
+ for (File file : files) {
+ deleteRecursively(file);
+ }
+ }
+
+ /**
+ * Deletes a file or directory and all contents recursively.
+ *
+ * <p>
+ * If the file argument is a symbolic link the link will be deleted but not
+ * the target of the link. If the argument is a directory, symbolic links
+ * within the directory will not be followed.
+ *
+ * @param file
+ * the file to delete
+ * @throws IOException
+ * if an I/O error occurs
+ * @see #deleteDirectoryContents
+ */
+ public static void deleteRecursively(File file) throws IOException {
+ if (file.isDirectory()) {
+ deleteDirectoryContents(file);
+ }
+ if (!file.delete()) {
+ throw new IOException("Failed to delete " + file);
+ }
+ }
+
+ public static String md5sum(File file) throws FileNotFoundException, IOException {
+ FileInputStream fis = new FileInputStream(file);
+ return md5sum(fis);
+ }
+
+ public static String md5sum(InputStream input) throws IOException {
+ try {
+ MessageDigest md;
+ md = MessageDigest.getInstance("MD5");
+ DigestInputStream dis = new DigestInputStream(input, md);
+ try {
+ byte[] buff = new byte[1024];
+ // just read to get the Digest filled...
+ while (dis.read(buff) > 0)
+ ;
+ return new BigInteger(1, md.digest()).toString(16);
+ } finally {
+ dis.close();
+ }
+ } catch (NoSuchAlgorithmException e) {
+ // this should not happen
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/LinkedHashSetBlockingQueue.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/LinkedHashSetBlockingQueue.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/LinkedHashSetBlockingQueue.java
new file mode 100644
index 0000000..8ff308e
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/LinkedHashSetBlockingQueue.java
@@ -0,0 +1,415 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util;
+
+import java.util.AbstractQueue;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * A blocking queue implementation backed by a linked hash set for predictable iteration order and
+ * constant time addition, removal and contains operations.
+ * <p/>
+ * Author: Sebastian Schaffert
+ */
+public class LinkedHashSetBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E> {
+
+ private int capacity = Integer.MAX_VALUE;
+
+ /** Current number of elements */
+ private final AtomicInteger count = new AtomicInteger(0);
+
+ /** Lock held by take, poll, etc */
+ private final ReentrantLock takeLock = new ReentrantLock();
+
+ /** Wait queue for waiting takes */
+ private final Condition notEmpty = takeLock.newCondition();
+
+ /** Lock held by put, offer, etc */
+ private final ReentrantLock putLock = new ReentrantLock();
+
+ /** Wait queue for waiting puts */
+ private final Condition notFull = putLock.newCondition();
+
+ private final LinkedHashSet<E> delegate;
+
+ public LinkedHashSetBlockingQueue() {
+ delegate = new LinkedHashSet<E>();
+ }
+
+ public LinkedHashSetBlockingQueue(int capacity) {
+ this.delegate = new LinkedHashSet<E>(capacity);
+ this.capacity = capacity;
+ }
+
+
+ @Override
+ public boolean offer(E e) {
+ if (e == null) throw new NullPointerException();
+ final AtomicInteger count = this.count;
+ if (count.get() == capacity)
+ return false;
+ int c = -1;
+ final ReentrantLock putLock = this.putLock;
+ putLock.lock();
+ try {
+ if (count.get() < capacity) {
+ final boolean wasAdded = enqueue(e);
+ c = wasAdded?count.getAndIncrement():count.get();
+ if (c + 1 < capacity)
+ notFull.signal();
+ }
+ } finally {
+ putLock.unlock();
+ }
+ if (c == 0)
+ signalNotEmpty();
+ return c >= 0;
+ }
+
+ @Override
+ public void put(E e) throws InterruptedException {
+ if (e == null) throw new NullPointerException();
+
+ int c = -1;
+ final ReentrantLock putLock = this.putLock;
+ final AtomicInteger count = this.count;
+ putLock.lockInterruptibly();
+ try {
+ while (count.get() == capacity) {
+ notFull.await();
+ }
+ final boolean wasAdded = enqueue(e);
+ c = wasAdded?count.getAndIncrement():count.get();
+ if (c + 1 < capacity)
+ notFull.signal();
+ } finally {
+ putLock.unlock();
+ }
+ if (c == 0)
+ signalNotEmpty();
+ }
+
+ @Override
+ public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
+ if (e == null) throw new NullPointerException();
+ long nanos = unit.toNanos(timeout);
+ int c = -1;
+ final ReentrantLock putLock = this.putLock;
+ final AtomicInteger count = this.count;
+ putLock.lockInterruptibly();
+ try {
+ while (count.get() == capacity) {
+
+ if (nanos <= 0)
+ return false;
+ nanos = notFull.awaitNanos(nanos);
+ }
+ final boolean wasAdded = enqueue(e);
+ c = wasAdded?count.getAndIncrement():count.get();
+ if (c + 1 < capacity)
+ notFull.signal();
+ } finally {
+ putLock.unlock();
+ }
+ if (c == 0)
+ signalNotEmpty();
+ return true;
+ }
+
+ @Override
+ public E take() throws InterruptedException {
+ E x;
+ int c = -1;
+ final AtomicInteger count = this.count;
+ final ReentrantLock takeLock = this.takeLock;
+ takeLock.lockInterruptibly();
+ try {
+ while (count.get() == 0) {
+ notEmpty.await();
+ }
+ x = dequeue();
+ c = count.getAndDecrement();
+ if (c > 1)
+ notEmpty.signal();
+ } finally {
+ takeLock.unlock();
+ }
+ if (c == capacity)
+ signalNotFull();
+ return x;
+ }
+
+ @Override
+ public E poll(long timeout, TimeUnit unit) throws InterruptedException {
+ E x = null;
+ int c = -1;
+ long nanos = unit.toNanos(timeout);
+ final AtomicInteger count = this.count;
+ final ReentrantLock takeLock = this.takeLock;
+ takeLock.lockInterruptibly();
+ try {
+ while (count.get() == 0) {
+ if (nanos <= 0)
+ return null;
+ nanos = notEmpty.awaitNanos(nanos);
+ }
+ x = dequeue();
+ c = count.getAndDecrement();
+ if (c > 1)
+ notEmpty.signal();
+ } finally {
+ takeLock.unlock();
+ }
+ if (c == capacity)
+ signalNotFull();
+ return x;
+ }
+
+ @Override
+ public int remainingCapacity() {
+ return Integer.MAX_VALUE - size();
+ }
+
+ @Override
+ public int drainTo(Collection<? super E> c) {
+ return drainTo(c,Integer.MAX_VALUE);
+ }
+
+ @Override
+ public int drainTo(Collection<? super E> c, int maxElements) {
+ if (c == null)
+ throw new NullPointerException();
+ if (c == this)
+ throw new IllegalArgumentException();
+ boolean signalNotFull = false;
+ final ReentrantLock takeLock = this.takeLock;
+ takeLock.lock();
+ try {
+ int n = Math.min(maxElements, count.get());
+ Iterator<E> it = delegate.iterator();
+ for(int i=0; i<n && it.hasNext(); i++) {
+ E x = it.next();
+ c.add(x);
+ }
+ count.getAndAdd(-n);
+ return n;
+ } finally {
+ takeLock.unlock();
+ if (signalNotFull)
+ signalNotFull();
+ }
+ }
+
+ @Override
+ public E poll() {
+ final AtomicInteger count = this.count;
+ if (count.get() == 0)
+ return null;
+ E x = null;
+ int c = -1;
+ final ReentrantLock takeLock = this.takeLock;
+ takeLock.lock();
+ try {
+ if (count.get() > 0) {
+ x = dequeue();
+ c = count.getAndDecrement();
+ if (c > 1)
+ notEmpty.signal();
+ }
+ } finally {
+ takeLock.unlock();
+ }
+ if (c == capacity)
+ signalNotFull();
+ return x;
+ }
+
+
+ @Override
+ public E peek() {
+ if (count.get() == 0)
+ return null;
+ final ReentrantLock takeLock = this.takeLock;
+ takeLock.lock();
+ try {
+ Iterator<E> it = delegate.iterator();
+ if(it.hasNext()) {
+ return it.next();
+ } else {
+ return null;
+ }
+ } finally {
+ takeLock.unlock();
+ }
+ }
+
+
+ /**
+ * Creates a node and links it at end of queue.
+ * @param x the item
+ * @return <code>true</code> if this set did not already contain <code>x</code>
+ */
+ private boolean enqueue(E x) {
+ synchronized (delegate) {
+ return delegate.add(x);
+ }
+ }
+
+ /**
+ * Removes a node from head of queue.
+ * @return the node
+ */
+ private E dequeue() {
+ synchronized (delegate) {
+ Iterator<E> it = delegate.iterator();
+ E x = it.next();
+ it.remove();
+ return x;
+ }
+ }
+
+
+
+ /**
+ * Lock to prevent both puts and takes.
+ */
+ void fullyLock() {
+ putLock.lock();
+ takeLock.lock();
+ }
+
+ /**
+ * Unlock to allow both puts and takes.
+ */
+ void fullyUnlock() {
+ takeLock.unlock();
+ putLock.unlock();
+ }
+
+ /**
+ * Signals a waiting take. Called only from put/offer (which do not
+ * otherwise ordinarily lock takeLock.)
+ */
+ private void signalNotEmpty() {
+ final ReentrantLock takeLock = this.takeLock;
+ takeLock.lock();
+ try {
+ notEmpty.signal();
+ } finally {
+ takeLock.unlock();
+ }
+ }
+
+ /**
+ * Signals a waiting put. Called only from take/poll.
+ */
+ private void signalNotFull() {
+ final ReentrantLock putLock = this.putLock;
+ putLock.lock();
+ try {
+ notFull.signal();
+ } finally {
+ putLock.unlock();
+ }
+ }
+
+ /**
+ * Tells whether both locks are held by current thread.
+ */
+ boolean isFullyLocked() {
+ return (putLock.isHeldByCurrentThread() &&
+ takeLock.isHeldByCurrentThread());
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ final Iterator<E> it = delegate.iterator();
+ return new Iterator<E>() {
+ @Override
+ public boolean hasNext() {
+ fullyLock();
+ try {
+ return it.hasNext();
+ } finally {
+ fullyUnlock();
+ }
+ }
+
+ @Override
+ public E next() {
+ fullyLock();
+ try {
+ return it.next();
+ } finally {
+ fullyUnlock();
+ }
+ }
+
+ @Override
+ public void remove() {
+ fullyLock();
+ try {
+ it.remove();
+ } finally {
+ fullyUnlock();
+ }
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return count.get();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (o == null) return false;
+
+ fullyLock();
+ try {
+ if(delegate.remove(o)) {
+ if(count.getAndDecrement() == capacity) {
+ notFull.signal();
+ }
+ return true;
+ }
+ } finally {
+ fullyUnlock();
+ }
+
+ return false;
+ }
+
+ @Override
+ public void clear() {
+ fullyLock();
+ try {
+ delegate.clear();
+ } finally {
+ fullyUnlock();
+ }
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/ReflectionUtils.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/ReflectionUtils.java
new file mode 100644
index 0000000..aa8b30e
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/ReflectionUtils.java
@@ -0,0 +1,160 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Utilities for working with reflection
+ *
+ * @author Sergio Fernández
+ *
+ */
+public class ReflectionUtils {
+
+ /**
+ * Get the actual class
+ *
+ * @param obj target object
+ * @return actual class
+ */
+ public static Class<?> getClass(Object obj) {
+ Class<?> cls = obj.getClass();
+ while (isProxied(cls)) {
+ cls = cls.getSuperclass();
+ }
+ return cls;
+ }
+
+ /**
+ * Check is the class is proxies
+ *
+ * @param cls class
+ * @return proxied
+ */
+ public static boolean isProxied(Class<?> cls) {
+ return cls.getName().contains("$$EnhancerByCGLIB$$") ||
+ cls.getName().contains("$$FastClassByCGLIB$$") ||
+ cls.getName().contains("_$$_javassist") ||
+ cls.getName().contains("_$$_WeldSubclass") ||
+ cls.getName().contains("$Proxy$");
+ }
+
+ /**
+ * Retrieve the value of the annotation
+ *
+ * @param obj target object
+ * @param annotation target annotation
+ * @param field annotation field
+ * @return value (null if object not annotated)
+ * @throws NoSuchMethodException
+ * @throws InvocationTargetException
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ */
+ public static Object getAnnotationValue(Object obj, Class<? extends Annotation> annotation, String field) throws IllegalArgumentException,
+ IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ Class<?> cls = getClass(obj);
+ if (cls.isAnnotationPresent(annotation))
+ return invokeMethod(cls.getAnnotation(annotation), field);
+ else
+ return null;
+ }
+
+ /**
+ * Get the method based on its name
+ *
+ * @param cls target class
+ * @param method method name
+ * @param params parameters
+ * @return method
+ * @throws NoSuchMethodException
+ */
+ public static Method getMethod(Class<?> cls, String method, Object[] params) throws NoSuchMethodException {
+ Class<?>[] classes = new Class[params.length];
+ for (int i = 0; i < params.length; i++) {
+ classes[i] = params[i].getClass();
+ }
+ return cls.getMethod(method, classes);
+ }
+
+ /**
+ * Invoke the method without parameters over the target object
+ *
+ * @param obj target object
+ * @param method method name
+ * @return value returned by the method invocation
+ * @throws IllegalArgumentException
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ * @throws NoSuchMethodException
+ */
+ public static Object invokeMethod(Object obj, String method) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException,
+ NoSuchMethodException {
+ return invokeMethod(obj, method, new Object[0]);
+ }
+
+ /**
+ * Invoke the method over the target object
+ *
+ * @param obj target object
+ * @param method method name
+ * @param params parameters
+ * @return value returned by the method invocation
+ * @throws IllegalArgumentException
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ * @throws NoSuchMethodException
+ */
+ public static Object invokeMethod(Object obj, String method, Object[] params) throws IllegalArgumentException, IllegalAccessException,
+ InvocationTargetException, NoSuchMethodException {
+ return invokeMethod(obj, getMethod(getClass(obj), method, params), params);
+ }
+
+ /**
+ * Invoke the method without parameters over the target object
+ *
+ * @param obj target object
+ * @param method method
+ * @return value returned by the method invocation
+ * @throws IllegalArgumentException
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ */
+ public static Object invokeMethod(Object obj, Method method) throws IllegalArgumentException, IllegalAccessException,
+ InvocationTargetException {
+ return invokeMethod(obj, method, new Object[0]);
+ }
+
+ /**
+ * Invoke the method over the target object
+ *
+ * @param obj target object
+ * @param method method
+ * @param params parameters
+ * @return value returned by the method invocation
+ * @throws IllegalArgumentException
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ */
+ public static Object invokeMethod(Object obj, Method method, Object[] params) throws IllegalArgumentException, IllegalAccessException,
+ InvocationTargetException {
+ return method.invoke(obj, params);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/WebServiceUtil.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/WebServiceUtil.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/WebServiceUtil.java
new file mode 100644
index 0000000..36511d6
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/WebServiceUtil.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util;
+
+import org.codehaus.jackson.map.ObjectMapper;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility methods for web services
+ * <p/>
+ * Author: Sebastian Schaffert
+ */
+public class WebServiceUtil {
+
+
+ /**
+ * Create a JSON representation of an exception, to be returned to the client. The JSON object will be formatted
+ * as follows:
+ * <code>
+ * {
+ * type: 'JAVA CLASS NAME',
+ * message: 'EXCEPTION MESSAGE'
+ * }
+ * </code>
+ */
+ public static String jsonErrorResponse(Exception ex) {
+ Map<String,Object> result = new HashMap<String, Object>();
+ result.put("type",ex.getClass().getSimpleName());
+ result.put("message",ex.getMessage());
+
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ return mapper.writeValueAsString(result);
+ } catch (IOException e) {
+ // cannot occur, we write to a string
+ return null;
+ }
+
+ }
+
+ /**
+ * Create a JSON representation of an exception, to be returned to the client. The JSON object will be formatted
+ * as follows:
+ * <code>
+ * {
+ * type: 'JAVA CLASS NAME',
+ * message: 'EXCEPTION MESSAGE'
+ * }
+ * </code>
+ */
+ public static void jsonErrorResponse(Exception ex, OutputStream out) throws IOException {
+ Map<String,Object> result = new HashMap<String, Object>();
+ result.put("type",ex.getClass().getSimpleName());
+ result.put("message",ex.getMessage());
+
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(out,result);
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/http/HttpRequestUtil.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/http/HttpRequestUtil.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/http/HttpRequestUtil.java
new file mode 100644
index 0000000..d0f3f65
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/util/http/HttpRequestUtil.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.util.http;
+
+import org.apache.marmotta.platform.core.api.http.HttpClientService;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.cache.HeaderConstants;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.params.CoreProtocolPNames;
+
+/**
+ * Utility methods, to be used in conjunction with {@link HttpClientService}.
+ *
+ */
+public class HttpRequestUtil {
+
+ private HttpRequestUtil() {
+ // static util class;
+ }
+
+ /**
+ * Configure whether redirects for this request should be handled automatically.
+ *
+ * @param request the request to modify
+ * @param followRedirect <code>true</code> if redirects (HTTP response code 3xx) should be
+ * handled automatically.
+ */
+ public static void setFollowRedirect(HttpRequestBase request, boolean followRedirect) {
+ request.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, followRedirect);
+ }
+
+ /**
+ * Set the <i>local part</i> of the User-Agent Header String. It will be suffixed with the
+ * global part of the User-Agent, which is handled by the corresponding
+ * {@link HttpClientService} implementation.
+ *
+ * @param request the request to modify
+ * @param userAgentString the prefix of the User-Agent string which will be suffixed with a
+ * LMF-global part handled b< the {@link HttpClientService} implementation.
+ */
+ public static void setUserAgentString(HttpRequestBase request, String userAgentString) {
+ request.getParams().setParameter(CoreProtocolPNames.USER_AGENT, userAgentString);
+ }
+
+ /**
+ * Set the <code>Cache-Control</code>-header for the provided request.
+ *
+ * @param request the request to modify
+ * @param cacheControl the cache-control directive
+ *
+ * @see HeaderConstants#CACHE_CONTROL_NO_CACHE
+ * @see HeaderConstants#CACHE_CONTROL_NO_STORE
+ * @see HeaderConstants#CACHE_CONTROL_MAX_AGE
+ * @see HeaderConstants#CACHE_CONTROL_MAX_STALE
+ * @see HeaderConstants#CACHE_CONTROL_MUST_REVALIDATE
+ * @see <a
+ * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">RFC2616#Cache-Control</a>
+ *
+ */
+ public static void setCacheControl(HttpRequestBase request, String cacheControl) {
+ if (cacheControl != null) {
+ request.setHeader(HttpHeaders.CACHE_CONTROL, cacheControl);
+ } else {
+ request.removeHeaders(HttpHeaders.CACHE_CONTROL);
+ }
+ }
+
+ /**
+ * Set the <code>Cache-Control</code>-header for the provided request to <code>no-cache</code>.
+ *
+ * @param request the request to modify
+ */
+ public static void setCacheControl_NoCache(HttpRequestBase request) {
+ setCacheControl(request, HeaderConstants.CACHE_CONTROL_NO_CACHE);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/CoreApplication.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/CoreApplication.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/CoreApplication.java
new file mode 100644
index 0000000..f337d06
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/CoreApplication.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.webservices;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.Application;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * CoreApplication
+ *
+ * TODO: manage this by reading kiwi-module.properties file in each of the KiWi modules and looking for a property
+ * "webservices"
+ *
+ * @author Sebastian Schaffert
+ *
+ */
+public class CoreApplication extends Application {
+
+ private Logger log = LoggerFactory.getLogger(CoreApplication.class);
+
+
+ private static Set<Class<?>> classes = null;
+
+ /**
+ *
+ */
+ public CoreApplication() {
+ super();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.ws.rs.core.Application#getClasses()
+ */
+ @Override
+ public synchronized Set<Class<?>> getClasses() {
+
+
+ if(classes == null) {
+ classes = new HashSet<Class<?>>();
+
+ try {
+ Enumeration<URL> modulePropertiesEnum = this.getClass().getClassLoader().getResources("kiwi-module.properties");
+
+ while(modulePropertiesEnum.hasMoreElements()) {
+ URL moduleUrl = modulePropertiesEnum.nextElement();
+
+ Configuration moduleProperties = null;
+ try {
+ moduleProperties = new PropertiesConfiguration(moduleUrl);
+
+ for(Object clsName : moduleProperties.getList("webservices")) {
+
+ if(!"".equals(clsName)) {
+ try {
+ Class<?> cls = Class.forName(clsName.toString());
+
+ classes.add(cls);
+
+ log.debug("module {}: registered webservice {}", moduleProperties.getString("name"), cls.getCanonicalName());
+ } catch (ClassNotFoundException e) {
+ log.error("could not load class {}, it was not found",clsName.toString());
+ }
+ }
+ }
+
+ } catch (ConfigurationException e) {
+ log.error("configuration exception: {}",e.getMessage());
+ }
+
+ }
+
+
+ } catch (IOException e) {
+ log.error("I/O error while trying to load kiwi-module.properties file: {}",e.getMessage());
+ }
+ }
+
+ return classes;
+ }
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/config/ConfigurationWebService.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/config/ConfigurationWebService.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/config/ConfigurationWebService.java
new file mode 100644
index 0000000..fa6c560
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/config/ConfigurationWebService.java
@@ -0,0 +1,238 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.webservices.config;
+
+import org.apache.marmotta.platform.core.api.config.ConfigurationService;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+import org.slf4j.Logger;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Manage the system configuration of the LMF Server. Provides methods for displaying and updating the configuration
+ * values.
+ */
+@ApplicationScoped
+@Path("/config")
+public class ConfigurationWebService {
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ @Inject
+ private Logger log;
+
+ /**
+ * Retrieve all entries in the system configuration as key-value pairs.
+ *
+ *
+ * @return a map mapping all configuration keys to the respective string or list values
+ * @HTTP 200 when the list of settings is retrieved successfully
+ */
+ @GET
+ @Path("/list")
+ @Produces("application/json")
+ public Map<String,Map<String,Object>> listConfiguration(@QueryParam("prefix")String prefix) {
+ HashMap<String,Map<String,Object>> result = new HashMap<String,Map<String,Object>>();
+ if(prefix==null) {
+ for(String key : configurationService.listConfigurationKeys()) {
+ Map<String,Object> config = new HashMap<String, Object>();
+ config.put("value",configurationService.getConfiguration(key));
+ config.put("comment",configurationService.getComment(key));
+ config.put("type",configurationService.getType(key));
+ result.put(key, config);
+ }
+ } else {
+ for(String key : configurationService.listConfigurationKeys(prefix)) {
+ Map<String,Object> config = new HashMap<String, Object>();
+ config.put("value",configurationService.getConfiguration(key));
+ config.put("comment",configurationService.getComment(key));
+ config.put("type",configurationService.getType(key));
+ result.put(key, config);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * stores a list of configuration
+ * @HTTP 200 if the configuration was set
+ * @HTTP 400 if the input sent in the body could not be parsed
+ * @param request
+ * @return HTTP 200 or 400
+ */
+ @POST
+ @Path("/list")
+ @Produces("application/json")
+ public Response setListConfiguration(@Context HttpServletRequest request) {
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ //log.info(getContentData(request.getReader()));
+ Map<String,String> values = mapper.readValue(request.getInputStream(), new TypeReference<HashMap<String,String>>(){});
+ configurationService.setConfigurations(values);
+ } catch (IOException e) {
+ return Response.serverError().build();
+ }
+ return Response.ok().build();
+ }
+
+ /**
+ * Return the string or list value for the configuration key passed as argument.
+ *
+ * @param key the configuration key for which to return the value
+ * @return the value of the requested configuration
+ * @HTTP 200 if the configuration key exists
+ * @HTTP 404 if the configuration key does not exist
+ */
+ @GET
+ @Path("/data/{key}")
+ @Produces("application/json")
+ public Response getConfiguration(@PathParam("key") String key) {
+ Object value = configurationService.getConfiguration(key);
+ if(value != null) {
+ HashMap<String,Object> result = new HashMap<String,Object>();
+ result.put(key, value);
+ return Response.status(200).entity(result).build();
+ } else
+ return Response.status(404).build();
+ }
+
+ /**
+ * Return the description for the configuration key passed as argument.
+ *
+ * @param key the configuration key for which to return the value
+ * @return the description of the requested configuration
+ * @HTTP 200 if the configuration key exists
+ * @HTTP 404 if the configuration key does not exist
+ */
+ @GET
+ @Path("/comment/{key}")
+ @Produces("application/json")
+ public Response getConfigurationComment(@PathParam("key") String key) {
+ String value = configurationService.getComment(key);
+ if(value != null) return Response.status(200).entity(Collections.singletonList(value)).build();
+ else
+ return Response.status(404).build();
+ }
+
+
+ /**
+ * Return the data type for the configuration key passed as argument.
+ *
+ * @param key the configuration key for which to return the value
+ * @return the data type of the requested configuration
+ * @HTTP 200 if the configuration key exists
+ * @HTTP 404 if the configuration key does not exist
+ */
+ @GET
+ @Path("/type/{key}")
+ @Produces("application/json")
+ public Response getConfigurationType(@PathParam("key") String key) {
+ String value = configurationService.getType(key);
+ if(value != null) return Response.status(200).entity(Collections.singletonList(value)).build();
+ else
+ return Response.status(404).build();
+ }
+
+ /**
+ * Set the configuration with the key passed in the path argument. The input has to be a
+ * JSON list of String values sent in the body of the request
+ *
+ * @param key the configuration key to set
+ * @param request the request body as JSON list
+ * @return OK if the value was set correctly, ERROR if parsing failed
+ * @HTTP 200 if the configuration was set
+ * @HTTP 400 if the input sent in the body could not be parsed
+ */
+ @POST
+ @Path("/data/{key}")
+ @Consumes("application/json")
+ public Response setConfiguration(@PathParam("key") String key, @QueryParam("type") String type, @QueryParam("comment") String comment, @Context HttpServletRequest request) {
+ try {
+ //log.info(getContentData(request.getReader()));
+ ObjectMapper mapper = new ObjectMapper();
+ List<String> values = mapper.readValue(request.getInputStream(), new TypeReference<ArrayList<String>>(){});
+ configurationService.setConfiguration(key,values);
+ if(type!=null) configurationService.setType(key,type);
+ if(comment!=null) configurationService.setComment(key,comment);
+ return Response.status(200).build();
+ } catch (JsonMappingException e) {
+ log.error("cannot parse input into json",e);
+ } catch (JsonParseException e) {
+ log.error("cannot parse input into json",e);
+ } catch (IOException e) {
+ log.error("cannot parse input into json",e);
+ }
+ return Response.status(400).build(); // bad request
+ }
+
+ /**
+ * Delete the configuration with the key passed in the path argument.
+ * @param key the configuration to remove
+ * @return 200 (OK) if the configuration was removed successfully, 500 (server error) otherwise
+ * @HTTP 200 if the configuration was removed successfully
+ * @HTTP 500 if there was an error while removing the configuration
+ */
+ @DELETE
+ @Path("/data/{key}")
+ public Response deleteConfiguration(@PathParam("key") String key) {
+ if(configurationService.getConfiguration(key) != null) {
+ try {
+ configurationService.removeConfiguration(key);
+ return Response.status(200).build();
+ } catch (Exception e) {
+ log.error("cannot delete configuration",e);
+ return Response.status(500).build();
+ }
+ } else
+ return Response.status(404).build();
+ }
+
+ public String getContent(BufferedReader r) {
+ String s;StringBuffer b = new StringBuffer();
+ try {
+ while((s = r.readLine()) != null) {
+ b.append(s);
+ }
+ } catch (IOException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ return b.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/config/DependenciesWebService.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/config/DependenciesWebService.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/config/DependenciesWebService.java
new file mode 100644
index 0000000..8e1b650
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/config/DependenciesWebService.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.webservices.config;
+
+import org.apache.marmotta.platform.core.api.config.DependenciesService;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Exposes details about the current core dependencies
+ *
+ * @author Sergio Fernández
+ */
+@ApplicationScoped
+@Path("/dependencies")
+public class DependenciesWebService {
+
+ @Inject
+ private DependenciesService dependenciesService;
+
+ @GET
+ @Path("/")
+ @Produces("application/json")
+ public List<Map<String, String>> getArtifacs() {
+ return dependenciesService.getArtifacs();
+ }
+
+ @GET
+ @Path("/{groupId: [a-z]+(\\.[a-z]+)*}")
+ @Produces("application/json")
+ public List<Map<String, String>> getArtifacs(@PathParam("groupId") String groupId) {
+ return dependenciesService.getArtifacs(groupId);
+ }
+
+ @GET
+ @Path("/{groupId: [a-z]+(\\.[a-z]+)*}/{artifactId: [a-z]+(\\.[a-z]+)*}")
+ @Produces("application/json")
+ public String getVersion(@PathParam("groupId") String groupId, @PathParam("artifactId") String artifactId) {
+ return dependenciesService.getVersion(groupId, artifactId);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/4d3eebdd/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/io/ExportWebService.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/io/ExportWebService.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/io/ExportWebService.java
new file mode 100644
index 0000000..e9f8384
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/io/ExportWebService.java
@@ -0,0 +1,169 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed 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.marmotta.platform.core.webservices.io;
+
+import static org.apache.marmotta.commons.sesame.repository.ExceptionUtils.handleRepositoryException;
+
+import org.apache.marmotta.commons.sesame.repository.ResourceUtils;
+import org.apache.marmotta.commons.util.DateUtils;
+import org.apache.marmotta.platform.core.api.exporter.ExportService;
+import org.apache.marmotta.platform.core.api.triplestore.SesameService;
+import org.apache.marmotta.platform.core.exception.io.UnsupportedExporterException;
+import org.apache.marmotta.commons.http.ContentType;
+import org.apache.marmotta.commons.http.LMFHttpUtils;
+import org.openrdf.model.URI;
+import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.rio.RDFFormat;
+import org.slf4j.Logger;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.*;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * A web service for exporting data from the LMF triple store
+ * <p/>
+ * Author: Sebastian Schaffert
+ */
+@ApplicationScoped
+@Path("/export")
+public class ExportWebService {
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private ExportService exportService;
+
+ @Inject
+ private SesameService sesameService;
+
+ /**
+ * Return a set of all mime types that are acceptable by the importer.
+ * @return a set of all mime types that are acceptable by the importer.
+ */
+ @GET
+ @Path("/types")
+ @Produces("application/json")
+ public List<String> getTypes() {
+ ArrayList<String> result = new ArrayList<String>(exportService.getProducedTypes());
+ Collections.sort(result);
+
+ return result;
+ }
+
+ /**
+ * Download the triple data contained in the (optional) context (named graph) in the format specified by the Accept
+ * header of the request. If the context parameter is not given, all triples contained in this LMF installation will
+ * be written to the response.
+ *
+ * @param types list of MIME types the client accepts
+ * @param context_string URI of the named graph to export; if null, all named graphs will be exported
+ * @param qFormat MIME type for return format, overrides accept header
+ * @return the HTTP response
+ * @throws IOException in case writing to the output stream of the connection fails
+ *
+ * @HTTP 200 in case the triples were written to the output stream correctly
+ * @HTTP 404 in case the context passed as argument could not be found
+ * @HTTP 406 in case the LMF could not find any matching serializer for the MIME types in the Accept header
+ */
+ @GET
+ @Path("/download")
+ public Response downloadData(@HeaderParam("Accept") String types, @QueryParam("format") String qFormat, @QueryParam("context") String context_string) throws IOException {
+ List<ContentType> acceptedTypes;
+ if(qFormat != null) {
+ acceptedTypes = LMFHttpUtils.parseAcceptHeader(qFormat);
+ } else {
+ acceptedTypes = LMFHttpUtils.parseAcceptHeader(types);
+ }
+ List<ContentType> offeredTypes = LMFHttpUtils.parseStringList(exportService.getProducedTypes());
+
+ final ContentType bestType = LMFHttpUtils.bestContentType(offeredTypes,acceptedTypes);
+
+ // create a file name for the export, preferrably with a good extension ...
+ String fileName;
+ if(context_string != null) {
+ String[] components = context_string.split("/");
+ fileName = components[components.length-1] + "-export-" + DateUtils.FILENAME_FORMAT.format(new Date());
+ } else {
+ fileName = "lmf-export-" + DateUtils.FILENAME_FORMAT.format(new Date());
+ }
+
+ if(bestType != null) {
+ RDFFormat format = RDFFormat.forMIMEType(bestType.getMime());
+ if(format != null) {
+ fileName += "." + format.getDefaultFileExtension();
+ }
+
+ URI context = null;
+ if(context_string != null) {
+ try {
+ RepositoryConnection conn = sesameService.getConnection();
+ try {
+ conn.begin();
+ context = ResourceUtils.getUriResource(conn,context_string);
+ } finally {
+ conn.commit();
+ conn.close();
+ }
+ } catch (RepositoryException e) {
+ handleRepositoryException(e,ExportWebService.class);
+ }
+
+ if(context == null) return Response.status(Response.Status.NOT_FOUND).entity("the context given as argument could not be found").build();
+
+ } else {
+ context = null;
+ }
+ final URI fcontext = context;
+
+ StreamingOutput entity = new StreamingOutput() {
+ @Override
+ public void write(OutputStream output) throws IOException, WebApplicationException {
+ try {
+ //FIXME: html should not be exported, but rendered?
+ exportService.exportData(output,fcontext,bestType.getMime());
+ } catch (UnsupportedExporterException e) {
+ throw new WebApplicationException(e, Response.Status.NOT_ACCEPTABLE);
+ }
+ }
+ };
+
+ return Response
+ .status(Response.Status.OK)
+ .header("Content-Type", bestType.getMime())
+ .header("Content-Disposition", "attachment; filename=\""+fileName+"\"")
+ .entity(entity)
+ .build();
+
+ } else
+ return Response.status(406)
+ .header("Content-Type", exportService.getProducedTypes())
+ .entity("could not find matching type for " + acceptedTypes + "; see Content-Type header for possible types")
+ .build();
+
+ }
+}