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();
+
+    }
+}