You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by wi...@apache.org on 2013/02/19 13:52:01 UTC
[9/52] [partial] code contribution,
initial import of relevant modules of LMF-3.0.0-SNAPSHOT based on
revision 4bf944319368 of the default branch at https://code.google.com/p/lmf/
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/services/triplestore/LdpServiceImpl.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/services/triplestore/LdpServiceImpl.java b/lmf-core/src/main/java/kiwi/core/services/triplestore/LdpServiceImpl.java
new file mode 100644
index 0000000..9bd9b30
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/core/services/triplestore/LdpServiceImpl.java
@@ -0,0 +1,357 @@
+/**
+ * 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 kiwi.core.services.triplestore;
+
+import static at.newmedialab.sesame.commons.repository.ExceptionUtils.handleRepositoryException;
+
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+import kiwi.core.api.config.ConfigurationService;
+import kiwi.core.api.prefix.PrefixService;
+import kiwi.core.api.triplestore.ContextService;
+import kiwi.core.api.triplestore.LdpService;
+import kiwi.core.api.triplestore.SesameService;
+
+import org.openrdf.model.Resource;
+import org.openrdf.model.URI;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.model.vocabulary.RDF;
+import org.openrdf.model.vocabulary.RDFS;
+import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.RepositoryException;
+import org.slf4j.Logger;
+
+import at.newmedialab.sesame.commons.repository.ResourceUtils;
+
+/**
+ * (Experimental) Implementation for supporting LDP (WIP)
+ *
+ * @author Sergio Fernández
+ *
+ */
+@ApplicationScoped
+public class LdpServiceImpl implements LdpService {
+
+ private URI context;
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ @Inject
+ private SesameService sesameService;
+
+ @Inject
+ private ContextService contextService;
+
+ @Inject
+ private PrefixService prefixService;
+
+ @PostConstruct
+ public void initialize() {
+ String uri = getBaseContainer();
+ try {
+ this.context = contextService.createContext(uri, "ldp (experimental context)");
+ createContainer(uri, "ldp base container");
+ } catch (URISyntaxException e) {
+ log.error("Root LDP Container {} cannot be created: {}", uri, e.getMessage());
+ }
+ }
+
+ @Override
+ public String getBaseContainer() {
+ return configurationService.getBaseContext() + ConfigurationService.LDP_PATH;
+ }
+
+ @Override
+ public List<URI> list() {
+ List<URI> containers = new ArrayList<URI>();
+ try {
+ RepositoryConnection conn = sesameService.getConnection();
+ try {
+ checkConnectionNamespace(conn);
+ conn.begin();
+ Iterable<Resource> results = ResourceUtils.listResources(conn, conn.getValueFactory().createURI(prefixService.getNamespace("ldp"), "Container"));
+ for (Resource result : results) {
+ if(result instanceof URI) {
+ containers.add((URI)result);
+ }
+ }
+ } finally {
+ conn.commit();
+ conn.close();
+ }
+ } catch (RepositoryException e) {
+ handleRepositoryException(e, LdpServiceImpl.class);
+ }
+ return containers;
+ }
+
+ @Override
+ public URI get(String uri) {
+ try {
+ RepositoryConnection conn = sesameService.getConnection();
+ try {
+ checkConnectionNamespace(conn);
+ conn.begin();
+ return (ResourceUtils.existsResource(conn, uri) ? ResourceUtils.getUriResource(conn, uri) : null);
+ } finally {
+ conn.commit();
+ conn.close();
+ }
+ } catch(RepositoryException ex) {
+ handleRepositoryException(ex, LdpServiceImpl.class);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean create(String uri) throws URISyntaxException {
+ return create(uri, null);
+ }
+
+ @Override
+ public boolean create(String uri, String title) throws URISyntaxException {
+ URI parent = getParentContainer(uri);
+ if (parent != null) {
+ return createResource(uri, parent);
+ } else {
+ throw new URISyntaxException(uri, "Non suitable parent container found");
+ }
+ }
+
+ private boolean createResource(String uri, URI container) {
+ //TODO: refactor this code, already implemented at the ResourceWebService,
+ //by moving all business logic to a service
+ try {
+ RepositoryConnection conn = sesameService.getConnection();
+ try {
+ checkConnectionNamespace(conn);
+ conn.begin();
+ ValueFactory factory = conn.getValueFactory();
+ URI resource = ResourceUtils.getUriResource(conn, uri);
+ //TODO: chek if already exists
+ conn.add(resource, RDF.TYPE, factory.createURI(prefixService.getNamespace("ldp"), "Resource"), container);
+ conn.add(resource, factory.createURI(prefixService.getNamespace("dct"), "created"), factory.createLiteral(new Date()), container);
+ conn.add(container, RDFS.MEMBER, resource, container);
+ return true; //FIXME: 201 vs 200
+ } finally {
+ conn.commit();
+ conn.close();
+ }
+ } catch (RepositoryException ex) {
+ return false;
+ }
+ }
+
+ private boolean createContainer(String container, String title) throws URISyntaxException {
+ try {
+ RepositoryConnection conn = sesameService.getConnection();
+ try {
+ checkConnectionNamespace(conn);
+ conn.begin();
+ ValueFactory factory = conn.getValueFactory();
+ URI uri = ResourceUtils.getUriResource(conn, container);
+ conn.add(uri, RDF.TYPE, factory.createURI(prefixService.getNamespace("ldp"), "Container"), context);
+ conn.add(uri, RDFS.LABEL, factory.createLiteral(title), context);
+ conn.add(uri, factory.createURI(prefixService.getNamespace("dct"), "created"), factory.createLiteral(new Date()), context);
+ log.info("Created new container <{}>", uri.stringValue());
+ return true;
+ } finally {
+ conn.commit();
+ conn.close();
+ }
+ } catch(RepositoryException ex) {
+ handleRepositoryException(ex, LdpServiceImpl.class);
+ }
+ return false;
+ }
+
+// @Override
+// public boolean addMember(String container, String member) {
+// //TODO: check parent relationship
+// try {
+// RepositoryConnection conn = sesameService.getConnection();
+// try {
+// checkConnectionNamespace(conn);
+// URI parent = ResourceUtils.getUriResource(conn, container);
+// URI uri = ResourceUtils.getUriResource(conn, member);
+// URI context = parent; //TODO: for the moment, every container is also a context (named graph) for us
+// conn.add(parent, RDFS.MEMBER, uri, context);
+// log.info("Created new container <{}>", uri.stringValue());
+// } finally {
+// conn.commit();
+// conn.close();path
+// }
+// return true;
+// } catch(RepositoryException ex) {
+// handleRepositoryException(ex, LdpServiceImpl.class);
+// return false;
+// }
+// }
+
+ @Override
+ public boolean delete(String uri) throws RepositoryException {
+ if (isContainer(uri)) {
+ return deleteContainer(uri);
+ } else {
+ return deleteResource(uri);
+ }
+ }
+
+ /**
+ * Resource deletion
+ *
+ * @todo refactor this code, already implemented at the ResourceWebService, by moving all business logic to a service
+ *
+ * @param uri
+ * @return
+ * @throws RepositoryException
+ */
+ private boolean deleteResource(String uri) throws RepositoryException {
+ //TODO: refactor this code, already implemented at the ResourceWebService,
+ //by moving all business logic to a service
+ RepositoryConnection conn = sesameService.getConnection();
+ URI parent;
+ try {
+ parent = getParentContainer(uri);
+ } catch (URISyntaxException e) {
+ parent = null;
+ }
+ try {
+ conn.begin();
+ URI resource = conn.getValueFactory().createURI(uri);
+ conn.remove(resource, null, null, parent);
+ return true;
+ } finally {
+ conn.commit();
+ conn.close();
+ }
+ }
+
+ /**
+ * Container deleteion
+ * (using a composition model)
+ *
+ * @param uri
+ * @return
+ * @throws RepositoryException
+ */
+ private boolean deleteContainer(String uri) throws RepositoryException {
+ RepositoryConnection conn = sesameService.getConnection();
+ try {
+ conn.begin();
+ URI context = conn.getValueFactory().createURI(uri);
+ conn.remove((Resource)null, null, null, context);
+ return true;
+ } finally {
+ conn.commit();
+ conn.close();
+ }
+ }
+
+ /**
+ * Check the connection's namespace, using the base context when needed
+ *
+ * @param conn
+ * @throws RepositoryException
+ */
+ private void checkConnectionNamespace(RepositoryConnection conn)
+ throws RepositoryException {
+ if(conn.getNamespace(DEFAULT_PREFIX) == null) {
+ conn.setNamespace(DEFAULT_PREFIX, getBaseContainer());
+ }
+ }
+
+ /**
+ * Check whenever this URI represents a PDPC
+ *
+ * @param uri
+ * @return
+ */
+ private boolean isContainer(String uri) {
+ try {
+ RepositoryConnection conn = sesameService.getConnection();
+ try {
+ checkConnectionNamespace(conn);
+ conn.begin();
+ Iterable<Resource> results = ResourceUtils.listResources(conn, conn.getValueFactory().createURI(prefixService.getNamespace("ldp"), "Container"), conn.getValueFactory().createURI(uri));
+ for (Resource result : results) {
+ if(result instanceof URI) {
+ if (uri.equals(result.stringValue())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ } finally {
+ conn.commit();
+ conn.close();
+ }
+ } catch (RepositoryException e) {
+ handleRepositoryException(e, LdpServiceImpl.class);
+ return false;
+ }
+ }
+
+ /**
+ * Get the parent container for a resource
+ *
+ * @param uri
+ * @return
+ * @throws URISyntaxException
+ */
+ private URI getParentContainer(String uri) throws URISyntaxException {
+ String base = this.getBaseContainer();
+ if (!uri.startsWith(base)) {
+ throw new URISyntaxException(uri, "Invalid URI: base URI does not matches with " + base);
+ }
+ if (base.equals(uri)) {
+ log.error("{} is already the base container", uri);
+ return null;
+ }
+ String parent = uri.substring(0, uri.lastIndexOf('/'));
+ try {
+ RepositoryConnection conn = sesameService.getConnection();
+ try {
+ checkConnectionNamespace(conn);
+ conn.begin();
+ if (!ResourceUtils.existsResource(conn, parent)) {
+ log.warn("Container {} does not exist, so creating it...", parent);
+ createContainer(parent, parent);
+ }
+ return ResourceUtils.getUriResource(conn, parent);
+ } finally {
+ conn.commit();
+ conn.close();
+ }
+ } catch (RepositoryException e) {
+ log.error("Error checking context {}: {}", parent, e.getMessage());
+ return null;
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/services/triplestore/SesameServiceImpl.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/services/triplestore/SesameServiceImpl.java b/lmf-core/src/main/java/kiwi/core/services/triplestore/SesameServiceImpl.java
new file mode 100644
index 0000000..7664cf7
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/core/services/triplestore/SesameServiceImpl.java
@@ -0,0 +1,313 @@
+/**
+ * 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 kiwi.core.services.triplestore;
+
+import kiwi.core.api.config.ConfigurationService;
+import kiwi.core.api.triplestore.NotifyingSailProvider;
+import kiwi.core.api.triplestore.SesameService;
+import kiwi.core.api.triplestore.StandardSailProvider;
+import kiwi.core.api.triplestore.TransactionalSailProvider;
+import kiwi.core.events.ConfigurationServiceInitEvent;
+import kiwi.core.events.SystemStartupEvent;
+import kiwi.core.qualifiers.event.transaction.AfterCommit;
+import kiwi.core.qualifiers.event.transaction.AfterRollback;
+import kiwi.core.qualifiers.event.transaction.BeforeCommit;
+import org.apache.marmotta.kiwi.persistence.KiWiDialect;
+import org.apache.marmotta.kiwi.persistence.h2.H2Dialect;
+import org.apache.marmotta.kiwi.persistence.mysql.MySQLDialect;
+import org.apache.marmotta.kiwi.persistence.pgsql.PostgreSQLDialect;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.transactions.api.TransactionListener;
+import org.apache.marmotta.kiwi.transactions.api.TransactionalSail;
+import org.apache.marmotta.kiwi.transactions.model.TransactionData;
+import org.apache.marmotta.kiwi.transactions.sail.KiWiTransactionalSail;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.RepositoryException;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.sail.NotifyingSail;
+import org.openrdf.sail.Sail;
+import org.slf4j.Logger;
+
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Event;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Instance;
+import javax.enterprise.inject.Produces;
+import javax.inject.Inject;
+
+/**
+ * Offers access to the Sesame repository underlying this LMF instance. The activation/deactivation methods
+ * of this service make sure the repository is properly initialised and shut down.
+ * <p/>
+ * Usage: to access the triple store properly through Sesame, you should follow the following
+ * pattern:
+ * <pre>
+ * RespositoryConnection con = sesameService.getConnection();
+ *
+ * URI subject = con.getValueFactory().createURI(...);
+ * ...
+ * RepositoryResult<Statement> result = con.getStatemenrs(subject,predicate,object,inferred,context);
+ * while(result.hasNext()) {
+ * Statement triple = result.next();
+ * ...
+ * }
+ *
+ * con.close();
+ * </pre>
+ *
+ * <p/>
+ * Will replace the existing TripleStore at some point.
+ * <p/>
+ * Author: Sebastian Schaffert
+ */
+@ApplicationScoped
+public class SesameServiceImpl implements SesameService {
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ @Inject @BeforeCommit
+ private Event<TransactionData> beforeCommitEvent;
+
+ @Inject @AfterCommit
+ private Event<TransactionData> afterCommitEvent;
+
+ @Inject @AfterRollback
+ private Event<TransactionData> afterRollbackEvent;
+
+ /**
+ * notifying sail providers from other modules
+ */
+ @Inject
+ private Instance<NotifyingSailProvider> notifyingSailProviders;
+
+ /**
+ * transactional sail providers from other modules
+ */
+ @Inject
+ private Instance<TransactionalSailProvider> transactionalSailProviders;
+
+ /**
+ * normal sail providers from other modules
+ */
+ @Inject
+ private Instance<StandardSailProvider> standardSailProviders;
+
+
+ private KiWiStore store;
+
+ private KiWiTransactionalSail tsail;
+
+ private SailRepository repository;
+
+ /**
+ * Initialise the Sesame repository. Should be called on service activation.
+ */
+ @Override
+ public synchronized void initialise() {
+ log.info("LMF Sesame Repository Service starting up ...");
+
+ if(repository != null) {
+ log.warn("RDF repository has already been initialized");
+ }
+
+ String database = configurationService.getStringConfiguration("database.type");
+ KiWiDialect dialect;
+ if("h2".equalsIgnoreCase(database)) {
+ dialect = new H2Dialect();
+ } else if("mysql".equalsIgnoreCase(database)) {
+ dialect = new MySQLDialect();
+ } else if("postgres".equalsIgnoreCase(database)) {
+ dialect = new PostgreSQLDialect();
+ } else
+ throw new IllegalStateException("database type "+database+" currently not supported!");
+ String jdbcUrl = configurationService.getStringConfiguration("database.url");
+ String dbUser = configurationService.getStringConfiguration("database.user");
+ String dbPass = configurationService.getStringConfiguration("database.password");
+
+ store = new KiWiStore("lmf", jdbcUrl, dbUser, dbPass, dialect, configurationService.getDefaultContext(), configurationService.getInferredContext());
+
+ tsail = new KiWiTransactionalSail(store);
+ tsail.addTransactionListener(new LMFTransactionEventProxy());
+
+
+ log.info("initialising repository plugins ...");
+
+ // wrap all stackable transactional sails
+ TransactionalSail transactionalSail = tsail;
+ for(TransactionalSailProvider provider : transactionalSailProviders) {
+ if(provider.isEnabled()) {
+ log.info("- transaction plugin: {}",provider.getName());
+ transactionalSail = provider.createSail(transactionalSail);
+ } else {
+ log.info("- transaction plugin: {} (DISABLED)", provider.getName());
+ }
+ }
+
+ // wrap all stackable notifying sails
+ NotifyingSail notifyingSail = transactionalSail;
+ for(NotifyingSailProvider provider : notifyingSailProviders) {
+ if(provider.isEnabled()) {
+ log.info("- notifying plugin: {}",provider.getName());
+ notifyingSail = provider.createSail(notifyingSail);
+ } else {
+ log.info("- notifying plugin: {} (DISABLED)", provider.getName());
+ }
+ }
+
+ // wrap all standard sails
+ Sail standardSail = notifyingSail;
+ for(StandardSailProvider provider : standardSailProviders) {
+ if(provider.isEnabled()) {
+ log.info("- standard plugin: {}",provider.getName());
+ standardSail = provider.createSail(standardSail);
+ } else {
+ log.info("- standard plugin: {} (DISABLED)", provider.getName());
+ }
+ }
+
+ repository = new SailRepository(standardSail);
+
+ try {
+ repository.initialize();
+ } catch (RepositoryException e) {
+ log.error("error while initialising LMF Sesame repository",e);
+ }
+ }
+
+ /**
+ * Shutdown the Sesame repository. Should be called on service deactivation.
+ */
+ @Override
+ @PreDestroy
+ public synchronized void shutdown() {
+ if(repository != null) {
+ log.info("LMF Sesame Repository Service shutting down ...");
+ try {
+ repository.shutDown();
+ } catch (RepositoryException e) {
+ log.error("error while shutting down LMF Sesame repository",e);
+ }
+ repository = null;
+ }
+ }
+
+ /**
+ * Reinit Sesame repository when the configuration has been initialised
+ *
+ * @param e
+ */
+ /*
+ public void onConfigurationChange(@Observes ConfigurationChangedEvent e) {
+ if(e.containsChangedKeyWithPrefix("database")) {
+ shutdown();
+ initialise();
+ }
+ }
+ */
+
+ /**
+ * Return the Sesame Repository underlying this service. Callers should be careful with modifying
+ * this object directly.
+ *
+ * @return the Sesame Repository instance used by this service
+ */
+ @Override
+ @Produces
+ public SailRepository getRepository() {
+ return repository;
+ }
+
+ /**
+ * Return a Sesame RepositoryConnection to the underlying repository. The connection has auto-commit disabled,
+ * all transaction management must be performed explicitly.
+ *
+ * @return a RepositoryConnection to the underlying Sesame repository.
+ */
+ @Override
+ @Produces
+ public RepositoryConnection getConnection() throws RepositoryException {
+ RepositoryConnection connection = repository.getConnection();
+ // connection.setAutoCommit(false);
+ return connection;
+ }
+
+ /**
+ * Return a Sesame ValueFactory for creating new RDF objects.
+ *
+ * @return the Sesame ValueFactory belonging to the repository that is used by the service
+ */
+ @Override
+ @Produces
+ public ValueFactory getValueFactory() {
+ return repository.getValueFactory();
+ }
+
+ private class LMFTransactionEventProxy implements TransactionListener {
+
+ /**
+ * Called before a transaction commits. The transaction data will contain all changes done in the transaction since
+ * the last commit. This method should be used in case the transaction listener aims to perform additional activities
+ * in the same transaction, like inserting or updating database tables.
+ * <p/>
+ * The implementation in lmf-core simply wraps the KiWi transaction data in a CDI transaction to notify other
+ * services in the system.
+ *
+ * @param data
+ */
+ @Override
+ public void beforeCommit(TransactionData data) {
+ log.debug("transaction: before commit event");
+ beforeCommitEvent.fire(data);
+ }
+
+ /**
+ * Called after a transaction has committed. The transaction data will contain all changes done in the transaction since
+ * the last commit. This method should be used in case the transaction listener aims to perform additional activities
+ * in a new transaction or outside the transaction management, e.g. notifying a server on the network, adding
+ * data to a cache, or similar.
+ * <p/>
+ * The implementation in lmf-core simply wraps the KiWi transaction data in a CDI transaction to notify other
+ * services in the system.
+ *
+ * @param data
+ */
+ @Override
+ public void afterCommit(TransactionData data) {
+ log.debug("transaction: after commit event");
+ afterCommitEvent.fire(data);
+ }
+
+ /**
+ * Called when a transaction rolls back.
+ * <p/>
+ * The implementation in lmf-core simply wraps the KiWi transaction data in a CDI transaction to notify other
+ * services in the system.
+ */
+ @Override
+ public void rollback(TransactionData data) {
+ log.debug("transaction: rollback event");
+ afterRollbackEvent.fire(data);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/services/user/UserServiceImpl.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/services/user/UserServiceImpl.java b/lmf-core/src/main/java/kiwi/core/services/user/UserServiceImpl.java
new file mode 100644
index 0000000..6c8a30f
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/core/services/user/UserServiceImpl.java
@@ -0,0 +1,372 @@
+/**
+ * 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 kiwi.core.services.user;
+
+import static at.newmedialab.sesame.commons.model.Namespaces.ADMIN_LOGIN;
+import static at.newmedialab.sesame.commons.model.Namespaces.ANONYMOUS_LOGIN;
+import static at.newmedialab.sesame.commons.repository.ExceptionUtils.handleRepositoryException;
+
+import at.newmedialab.sesame.commons.repository.ResourceUtils;
+import at.newmedialab.sesame.facading.FacadingFactory;
+import kiwi.core.api.config.ConfigurationService;
+import kiwi.core.api.triplestore.SesameService;
+import kiwi.core.api.user.UserService;
+import kiwi.core.events.SystemRestartedEvent;
+import kiwi.core.events.SystemRestartingEvent;
+import kiwi.core.exception.UserExistsException;
+import kiwi.core.model.user.KiWiUser;
+import kiwi.core.qualifiers.user.AdminUser;
+import kiwi.core.qualifiers.user.AnonymousUser;
+import kiwi.core.qualifiers.user.CurrentUser;
+import org.openrdf.model.URI;
+import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.RepositoryException;
+import org.slf4j.Logger;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Produces;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Add file description here!
+ * <p/>
+ * User: sschaffe
+ */
+@Named("kiwi.core.userService")
+@ApplicationScoped
+public class UserServiceImpl implements UserService {
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private SesameService sesameService;
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ /**
+ * Each thread gets its own User. By using {@link InheritableThreadLocal}, the user is inherited
+ * by from the parent thread unless it is explicitly set.
+ */
+ private static InheritableThreadLocal<URI> currentUser = new InheritableThreadLocal<URI>();
+
+ // marker to ensure that no other thread interferes while setting up default users ...
+ private boolean initialised = false;
+ private boolean users_created = false;
+
+ private final Lock lock = new ReentrantLock();
+
+
+ private URI adminUser;
+ private URI anonUser;
+
+
+ /**
+ * initialize() initializes the anonymous and admin user
+ * if the database has not yet been set up.
+ */
+ @PostConstruct
+ public void initialize() {
+ log.info("initialising user service ...");
+ }
+
+
+ /**
+ * Create the admin and anonymous user in the database. Must be called before any other method can be used.
+ */
+ @Override
+ public void createDefaultUsers() {
+ lock.lock();
+ try {
+ initialised = true; // set marker so that functions can work; we still hold the lock!
+
+ if (!userExists(ANONYMOUS_LOGIN)) {
+ log.debug("Initializing anonymous user.");
+ try {
+ anonUser = createUser(ANONYMOUS_LOGIN);
+ } catch(UserExistsException e) {
+ log.debug("Anonymous user already exists.");
+ }
+ } else {
+ anonUser = getUserByLogin(ANONYMOUS_LOGIN);
+ }
+
+ if (!userExists(ADMIN_LOGIN)) {
+ log.debug("Initializing admin.");
+ try {
+ adminUser = createUser(ADMIN_LOGIN);
+ } catch(UserExistsException e) {
+ log.debug("Admin already exists.");
+ }
+ } else {
+ adminUser = getUserByLogin(ADMIN_LOGIN);
+ }
+
+ users_created = true;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * When system is restarted, flush all cache data
+ * @param e
+ */
+ public void systemRestart(@Observes SystemRestartingEvent e) {
+ log.warn("system restarted, clearing user caches");
+
+ clearCurrentUser();
+ adminUser = null;
+ anonUser = null;
+ initialised = false;
+ users_created = false;
+ }
+
+ /**
+ * When database is re-initialised, create default users
+ * @param e
+ */
+ public void systemRestarted(@Observes SystemRestartedEvent e) {
+ log.warn("system finished restarting, recreating default users");
+ createDefaultUsers();
+ }
+
+ /**
+ * Return the currently active user. The method tries to determine the current user using the following means:
+ * - the user stored in the session, if existent
+ * - the user authenticated using HTTP authentication, if existent
+ * - the anonymous user
+ *
+ * @return
+ */
+ @Override
+ @Produces @CurrentUser
+ public URI getCurrentUser() {
+ if(currentUser.get() == null)
+ return getAnonymousUser();
+ else
+ return currentUser.get();
+ }
+
+
+
+ /**
+ * Set the current user to the user passed as argument. The current user should be associated with
+ * the current session in a thread local variable that is cleared again when the request finishes
+ * (KiWiUserTokenFilter)
+ *
+ * @param user - the resource that represents the user
+ */
+ @Override
+ public void setCurrentUser(URI user) {
+ currentUser.set(user);
+ }
+
+ /**
+ * Clear a current user setting for the current thread. Clears the thread local variable set for the
+ * currently running thread.
+ */
+ @Override
+ public void clearCurrentUser() {
+ currentUser.remove();
+ }
+
+
+
+ /**
+ * Return the anonymous user. If it does not exist yet, it is created in the database and stored.
+ *
+ * @return
+ */
+ @Override
+ @Produces @AnonymousUser
+ public URI getAnonymousUser() {
+ while(anonUser == null && !users_created) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {}
+ log.warn("anonymous user not yet created; waiting for creation to finish");
+ }
+
+ return anonUser;
+ }
+
+ @Override
+ public boolean isAnonymous(URI user) {
+ return getAnonymousUser().equals(user);
+ }
+
+ @Override
+ @Produces @AdminUser
+ public URI getAdminUser() {
+ while(adminUser == null && !users_created) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {}
+ log.warn("admin user not yet created; waiting for creation to finish");
+ }
+
+ return adminUser;
+ }
+
+
+ /**
+ * Create a new user with the provided login. The method first
+ * checks of a user with this login already exists; if yes, an exception is thrown. The
+ * method does not persist the user; this needs to be done by the caller.
+ *
+ *
+ *
+ * @param login login of the user to create
+ * @return the newly created user.
+ */
+ @Override
+ public URI createUser(String login) throws UserExistsException {
+ return createUser(login, null, null);
+ }
+
+
+
+ /**
+ * Create a new user with the provided login, first name and last name. The method first
+ * checks of a user with this login already exists; if yes, an exception is thrown. The
+ * method does not persist the user; this needs to be done by the caller.
+ *
+ *
+ *
+ * @param login login of the user to create
+ * @param firstName first name of the user to create
+ * @param lastName last name of the user to create
+ * @return the newly created user.
+ */
+ @Override
+ public URI createUser(final String login, final String firstName, final String lastName) throws UserExistsException {
+ lock.lock();
+ try {
+ if(!userExists(login)) {
+
+ String webId_str = buildUserUri(login);
+
+ try {
+ RepositoryConnection conn = sesameService.getConnection();
+
+ URI webId = conn.getValueFactory().createURI(webId_str);
+
+ log.info("creating user with webId: {} ", webId.stringValue());
+
+ if (!login.equals(ANONYMOUS_LOGIN) && !login.equals(ADMIN_LOGIN)) {
+ KiWiUser u = FacadingFactory.createFacading(conn).createFacade(webId, KiWiUser.class);
+ u.setFirstName(firstName);
+ u.setLastName(lastName);
+ u.setNick(login);
+ }
+
+ if(login.equals(ANONYMOUS_LOGIN)) {
+ anonUser = webId;
+ }
+ if(login.equals(ADMIN_LOGIN)) {
+ adminUser = webId;
+ }
+
+ conn.commit();
+ conn.close();
+
+ return webId;
+ } catch(RepositoryException ex) {
+ handleRepositoryException(ex);
+ return null;
+ }
+
+ } else
+ throw new UserExistsException("User "+login+" already exists, cannot create!");
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private String buildUserUri(final String login) {
+ return configurationService.getBaseUri() + "user/" + login;
+ }
+
+
+ /**
+ * Return a user by login. The user is looked up in the database and returned. In case
+ * no user with this login is found, this method returns null.
+ *
+ *
+ *
+ * @param login the login to look for
+ * @return the user with the given login, or null if no such user exists
+ */
+ @Override
+ public URI getUser(String login) {
+ return getUserByLogin(login);
+ }
+
+ /**
+ * Return a user by login. The user is looked up in the database and returned. In case
+ * no user with this login is found, this method returns null. The second parameter
+ * specifies whether a failure should be logged or accepted silently. A silent progress
+ * may be useful in case we just want to check whether a user exists.
+ *
+ *
+ * @param login the login to look for
+ * @return the user with the given login, or null if no such user exists
+ */
+ private URI getUserByLogin(String login) {
+ try {
+ RepositoryConnection conn = sesameService.getConnection();
+ try {
+ conn.begin();
+ String userUri = buildUserUri(login);
+ if (ResourceUtils.existsResource(conn, userUri))
+ return conn.getValueFactory().createURI(userUri);
+ else
+ return null;
+ } finally {
+ // TODO: this is not the perfect way to do it, but the only way to avoid cycles
+ // with the versioning
+ conn.rollback();
+ conn.close();
+ }
+ } catch (RepositoryException e) {
+ handleRepositoryException(e, UserServiceImpl.class);
+
+ return null;
+ }
+ }
+
+ /**
+ * Check whether a user with the given login already exists. If so,
+ * returns true. Otherwise returns false.
+ *
+ * @param login the login to look for
+ * @return true if a user with this login already exists, false otherwise
+ * @see kiwi.core.api.user.UserService#userExists(java.lang.String)
+ */
+ @Override
+ public boolean userExists(String login) {
+ return getUserByLogin(login) != null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/servlet/KiWiH2ConsoleFilter.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/servlet/KiWiH2ConsoleFilter.java b/lmf-core/src/main/java/kiwi/core/servlet/KiWiH2ConsoleFilter.java
new file mode 100644
index 0000000..7e0f641
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/core/servlet/KiWiH2ConsoleFilter.java
@@ -0,0 +1,89 @@
+/**
+ * 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 kiwi.core.servlet;
+
+import kiwi.core.api.config.ConfigurationService;
+import org.slf4j.Logger;
+
+import javax.inject.Inject;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * This filter injects the database configuration into the H2 console using the LMF Configuration settings
+ * <p/>
+ * User: sschaffe
+ */
+public class KiWiH2ConsoleFilter implements Filter {
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+
+
+ if(servletRequest instanceof HttpServletRequest) {
+ HttpServletRequest req = (HttpServletRequest)servletRequest;
+
+ if(req.getPathInfo() != null && req.getPathInfo().endsWith("login.jsp")) {
+ // we redirect immediately to the action, since the login screen is not needed
+ String origUri = req.getRequestURI();
+ String redirectUri = origUri.substring(0,origUri.lastIndexOf("/")) + "/login.do?jsessionid="+req.getParameter("jsessionid");
+
+ ((HttpServletResponse)servletResponse).sendRedirect(redirectUri);
+ }
+
+ if(req.getPathInfo() != null && req.getPathInfo().endsWith("login.do")) {
+ // set the database configuration from system configuration
+ String db_type = configurationService.getStringConfiguration("database.type","h2");
+ String db_driver = configurationService.getStringConfiguration("database."+db_type+".driver");
+ String db_url = configurationService.getStringConfiguration("database.url");
+ String db_user = configurationService.getStringConfiguration("database.user");
+ String db_password = configurationService.getStringConfiguration("database.password");
+
+ servletRequest.setAttribute("driver",db_driver);
+ servletRequest.setAttribute("url",db_url);
+ servletRequest.setAttribute("user",db_user);
+ servletRequest.setAttribute("password",db_password);
+ }
+ }
+
+ filterChain.doFilter(servletRequest,servletResponse);
+
+
+ }
+
+ @Override
+ public void destroy() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/servlet/KiWiPostStartupFilter.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/servlet/KiWiPostStartupFilter.java b/lmf-core/src/main/java/kiwi/core/servlet/KiWiPostStartupFilter.java
new file mode 100644
index 0000000..1f22d95
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/core/servlet/KiWiPostStartupFilter.java
@@ -0,0 +1,121 @@
+/**
+ * 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 kiwi.core.servlet;
+
+import java.io.IOException;
+
+import javax.enterprise.event.Event;
+import javax.enterprise.inject.Any;
+import javax.inject.Inject;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import kiwi.core.api.config.ConfigurationService;
+import kiwi.core.api.user.UserService;
+import kiwi.core.events.SystemStartupEvent;
+import kiwi.core.model.module.ModuleConfiguration;
+
+import org.slf4j.Logger;
+
+/**
+ * This filter is currently only used to indicate that the LMF server has been started successfully.
+ * <p/>
+ * User: sschaffe
+ */
+public class KiWiPostStartupFilter implements Filter {
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ @Inject
+ private UserService userService;
+
+ @Inject
+ private ModuleConfiguration moduleConfiguration;
+
+ @Inject @Any
+ private Event<SystemStartupEvent> startupEvent;
+
+ /**
+ * Called by the web container to indicate to a filter that it is being placed into
+ * service. The servlet container calls the init method exactly once after instantiating the
+ * filter. The init method must complete successfully before the filter is asked to do any
+ * filtering work. <br><br>
+ * <p/>
+ * The web container cannot place the filter into service if the init method either<br>
+ * 1.Throws a ServletException <br>
+ * 2.Does not return within a time period defined by the web container
+ */
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ // if the system is already configured, we can safely trigger a startup event
+ if(configurationService.getBooleanConfiguration("kiwi.setup.host",false)) {
+ // startupEvent.fire(new SystemStartupEvent());
+ }
+
+ if(moduleConfiguration.hasBuildInfo()) {
+ log.warn("LMF Core Version {} has started up successfully!", moduleConfiguration.getModuleVersion());
+ } else {
+ log.warn("LMF Core (Development Version) has started up successfully!");
+ }
+
+ log.warn("You can access the system now at the URL {}", "http://"+configurationService.getServerName()+":"+configurationService.getServerPort()+configurationService.getServerContext()+"/");
+ }
+
+ /**
+ * The <code>doFilter</code> method of the Filter is called by the container
+ * each time a request/response pair is passed through the chain due
+ * to a client request for a resource at the end of the chain. The FilterChain passed in to this
+ * method allows the Filter to pass on the request and response to the next entity in the
+ * chain.<p>
+ * A typical implementation of this method would follow the following pattern:- <br>
+ * 1. Examine the request<br>
+ * 2. Optionally wrap the request object with a custom implementation to
+ * filter content or headers for input filtering <br>
+ * 3. Optionally wrap the response object with a custom implementation to
+ * filter content or headers for output filtering <br>
+ * 4. a) <strong>Either</strong> invoke the next entity in the chain using the FilterChain object (<code>chain.doFilter()</code>), <br>
+ * * 4. b) <strong>or</strong> not pass on the request/response pair to the next entity in the filter chain to block the request processing<br>
+ * * 5. Directly set headers on the response after invocation of the next entity in the filter chain.
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ chain.doFilter(request, response);
+ }
+
+ /**
+ * Called by the web container to indicate to a filter that it is being taken out of service. This
+ * method is only called once all threads within the filter's doFilter method have exited or after
+ * a timeout period has passed. After the web container calls this method, it will not call the
+ * doFilter method again on this instance of the filter. <br><br>
+ * <p/>
+ * This method gives the filter an opportunity to clean up any resources that are being held (for
+ * example, memory, file handles, threads) and make sure that any persistent state is synchronized
+ * with the filter's current state in memory.
+ */
+ @Override
+ public void destroy() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/servlet/KiWiPreStartupFilter.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/servlet/KiWiPreStartupFilter.java b/lmf-core/src/main/java/kiwi/core/servlet/KiWiPreStartupFilter.java
new file mode 100644
index 0000000..7c22180
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/core/servlet/KiWiPreStartupFilter.java
@@ -0,0 +1,116 @@
+/**
+ * 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 kiwi.core.servlet;
+
+import kiwi.core.api.config.ConfigurationService;
+import kiwi.core.startup.LMFStartupService;
+import org.slf4j.Logger;
+
+import javax.inject.Inject;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * This filter is evaluated when the LMF system is accessed for the very first time using an HTTP client (e.g. a
+ * browser). Its purpose is to set configuration variables that cannot be determined when the server is starting
+ * up because they need information how the server is accessed. In particular, it will set the following
+ * configuration variables in case the system has not yet been configured:
+ * <ul>
+ * <li>kiwi.context - will be set to the base URI of this KiWi installation; used for constructing resource URIs</li>
+ * <li>kiwi.host - will be set to the base URL of the KiWi installation; used for accessing additional web services like SOLR and H2</li>
+ * </ul>
+ * <p/>
+ * User: Sebastian Schaffert
+ */
+public class KiWiPreStartupFilter implements Filter {
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private LMFStartupService startupService;
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ private boolean started = false;
+
+ /**
+ * If the hostname (config properties kiwi.host and kiwi.context) of the system has not been configured yet, we
+ * try to automatically determine the correct values from the first request. The config variable
+ * kiwi.setup.host indicates whether setup has already been carried out.
+ * @param request
+ */
+ private String getBaseUrl(ServletRequest request) {
+ // check whether we need to perform setup
+ if (request instanceof HttpServletRequest) {
+ HttpServletRequest hreq = (HttpServletRequest) request;
+
+ String contextName = hreq.getContextPath();
+ String hostName = hreq.getServerName();
+ int hostPort = hreq.getServerPort();
+ String hostScheme = hreq.getScheme();
+
+ String baseUrl = hostScheme + "://" + hostName;
+ if( hostPort == 80 && "http".equals(hostScheme) ||
+ hostPort == 443 && "https".equals(hostScheme) ) {
+ baseUrl = baseUrl + contextName + "/";
+ } else {
+ baseUrl = baseUrl + ":" + hostPort + contextName + "/";
+ }
+
+ return baseUrl;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+
+ if(!started) {
+ synchronized (this) {
+ started = true;
+
+ String baseUrl = getBaseUrl(request);
+ if(baseUrl != null) {
+ startupService.startupHost(baseUrl, baseUrl);
+ } else {
+ log.error("could not determine host name; cannot startup LMF");
+ }
+ }
+ }
+
+ filterChain.doFilter(request,servletResponse);
+ }
+
+
+ @Override
+ public void destroy() {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/servlet/KiWiPreStartupListener.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/servlet/KiWiPreStartupListener.java b/lmf-core/src/main/java/kiwi/core/servlet/KiWiPreStartupListener.java
new file mode 100644
index 0000000..c811ae4
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/core/servlet/KiWiPreStartupListener.java
@@ -0,0 +1,61 @@
+/**
+ * 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 kiwi.core.servlet;
+
+import kiwi.core.startup.LMFStartupService;
+import kiwi.core.util.KiWiContext;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/**
+ * This filter is executed first in the startup chain. It initialises the LMF system variables if necessary and
+ * starts up the configuration service.
+ */
+public class KiWiPreStartupListener implements ServletContextListener {
+
+ private LMFStartupService lmfStartupService;
+
+ /**
+ * * Notification that the web application initialization
+ * * process is starting.
+ * * All ServletContextListeners are notified of context
+ * * initialization before any filter or servlet in the web
+ * * application is initialized.
+ */
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+
+ if(lmfStartupService == null) {
+ lmfStartupService = KiWiContext.getInstance(LMFStartupService.class);
+ }
+
+ lmfStartupService.startupConfiguration(null,null,sce.getServletContext());
+
+ }
+
+ /**
+ * * Notification that the servlet context is about to be shut down.
+ * * All servlets and filters have been destroy()ed before any
+ * * ServletContextListeners are notified of context
+ * * destruction.
+ */
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+ lmfStartupService.shutdown();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/servlet/KiWiResourceFilter.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/servlet/KiWiResourceFilter.java b/lmf-core/src/main/java/kiwi/core/servlet/KiWiResourceFilter.java
new file mode 100644
index 0000000..b418bae
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/core/servlet/KiWiResourceFilter.java
@@ -0,0 +1,218 @@
+/**
+ * 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 kiwi.core.servlet;
+
+import kiwi.core.api.config.ConfigurationService;
+import kiwi.core.api.modules.LMFHttpFilter;
+import kiwi.core.api.modules.ModuleService;
+import org.slf4j.Logger;
+
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.Instance;
+import javax.inject.Inject;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This filter is used by KiWi for initialisation of the KiWi system on startup of the server. It does not perform
+ * any actual filtering. For this purpose, a listener would have been better, but CDI in Jetty does not support
+ * injection into listeners, so we "abuse" a filter for this purpose. Filters always get initialised before servlets,
+ * so by adding the KiWiFilter as the first entry into web.xml, we can ensure that the KiWi initialisation is done
+ * before everything else.
+ * <p/>
+ * User: sschaffe
+ */
+public class KiWiResourceFilter implements Filter {
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ @Inject
+ private ModuleService moduleService;
+
+ @Inject @Any
+ private Instance<LMFHttpFilter> filters;
+
+ private List<LMFHttpFilter> filterList;
+
+ /**
+ * Called by the web container to indicate to a filter that it is being placed into
+ * service. The servlet container calls the init method exactly once after instantiating the
+ * filter. The init method must complete successfully before the filter is asked to do any
+ * filtering work. <br><br>
+ * <p/>
+ * The web container cannot place the filter into service if the init method either<br>
+ * 1.Throws a ServletException <br>
+ * 2.Does not return within a time period defined by the web container
+ */
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ log.info("LMF Resource Filter {} starting up ... ", configurationService.getConfiguration("kiwi.version"));
+
+
+ // initialise filter chain and sort it according to priority
+ this.filterList = new ArrayList<LMFHttpFilter>();
+
+ for(LMFHttpFilter filter : filters) {
+ try {
+ filter.init(filterConfig);
+ filterList.add(filter);
+
+ log.debug("module {}: registered filter {}", moduleService.getModuleConfiguration(filter.getClass()).getModuleName(), filter.getClass().getCanonicalName());
+ } catch (ServletException ex) {
+ log.error("could not instantiate filter {}, servlet exception during initialisation ({})",filter.getClass(),ex.getMessage());
+ }
+ }
+
+ Collections.sort(filterList,new FilterComparator());
+
+ }
+
+
+
+ /**
+ * The <code>doFilter</code> method of the Filter is called by the container
+ * each time a request/response pair is passed through the chain due
+ * to a client request for a resource at the end of the chain. The FilterChain passed in to this
+ * method allows the Filter to pass on the request and response to the next entity in the
+ * chain.<p>
+ * A typical implementation of this method would follow the following pattern:- <br>
+ * 1. Examine the request<br>
+ * 2. Optionally wrap the request object with a custom implementation to
+ * filter content or headers for input filtering <br>
+ * 3. Optionally wrap the response object with a custom implementation to
+ * filter content or headers for output filtering <br>
+ * 4. a) <strong>Either</strong> invoke the next entity in the chain using the FilterChain object (<code>chain.doFilter()</code>), <br>
+ * * 4. b) <strong>or</strong> not pass on the request/response pair to the next entity in the filter chain to block the request processing<br>
+ * * 5. Directly set headers on the response after invocation of the next entity in the filter chain.
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ URL url = null;
+ String prefix = null, path = null;
+ if (request instanceof HttpServletRequest) {
+ url = new URL(((HttpServletRequest)request).getRequestURL().toString());
+ prefix = ((HttpServletRequest)request).getContextPath();
+ if(url.getPath().startsWith(prefix)) {
+ path = url.getPath().substring(prefix.length());
+ }
+ }
+
+ new LMFFilterChain(path,chain).doFilter(request,response);
+
+ }
+
+
+
+
+ /**
+ * Called by the web container to indicate to a filter that it is being taken out of service. This
+ * method is only called once all threads within the filter's doFilter method have exited or after
+ * a timeout period has passed. After the web container calls this method, it will not call the
+ * doFilter method again on this instance of the filter. <br><br>
+ * <p/>
+ * This method gives the filter an opportunity to clean up any resources that are being held (for
+ * example, memory, file handles, threads) and make sure that any persistent state is synchronized
+ * with the filter's current state in memory.
+ */
+ @Override
+ public void destroy() {
+ for(LMFHttpFilter filter : filterList) {
+ filter.destroy();
+ }
+ }
+
+
+ /**
+ * A special filter chain that implements the LMFHttpFilter calls
+ */
+ private class LMFFilterChain implements FilterChain {
+
+ private Iterator<LMFHttpFilter> filters;
+
+ private String path;
+
+ private FilterChain originalChain;
+
+ LMFFilterChain(String path, FilterChain originalChain) {
+ this.path = path;
+ this.filters = filterList.iterator();
+ this.originalChain = originalChain;
+ }
+
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+ if(filters.hasNext()) {
+ LMFHttpFilter filter = filters.next();
+
+ if(path.matches(filter.getPattern())) {
+ filter.doFilter(request,response,this);
+ } else {
+ doFilter(request,response);
+ }
+ } else {
+ originalChain.doFilter(request,response);
+ }
+ }
+ }
+
+
+
+ private static class FilterComparator implements Comparator<LMFHttpFilter>, Serializable {
+
+ private static final long serialVersionUID = -7264645592168345092L;
+
+ /**
+ * Compares its two arguments for order. Returns a negative integer,
+ * zero, or a positive integer as the first argument is less than, equal
+ * to, or greater than the second.<p>
+ * <p/>
+ *
+ * @param o1 the first object to be compared.
+ * @param o2 the second object to be compared.
+ * @return a negative integer, zero, or a positive integer as the
+ * first argument is less than, equal to, or greater than the
+ * second.
+ * @throws ClassCastException if the arguments' types prevent them from
+ * being compared by this comparator.
+ */
+ @Override
+ public int compare(LMFHttpFilter o1, LMFHttpFilter o2) {
+ if(o1.getPriority() < o2.getPriority())
+ return -1;
+ else if(o1.getPriority() > o2.getPriority()) return 1;
+ else
+ return 0;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/servlet/LMFOptionsFilter.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/servlet/LMFOptionsFilter.java b/lmf-core/src/main/java/kiwi/core/servlet/LMFOptionsFilter.java
new file mode 100644
index 0000000..da274bc
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/core/servlet/LMFOptionsFilter.java
@@ -0,0 +1,168 @@
+/**
+ * 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 kiwi.core.servlet;
+
+import kiwi.core.api.config.ConfigurationService;
+import org.jboss.resteasy.spi.DefaultOptionsMethodException;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.slf4j.Logger;
+
+import javax.inject.Inject;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * This filter checks for OPTIONS requests. If the response returned by the other filters throws an exception
+ * org.jboss.resteasy.spi.DefaultOptionsMethodException, the filter writes to the response the default options
+ * of the LMF system, in particular the following headers:
+ *
+ * Access-Control-Allow-Origin: * (or as configured in config.properties)
+ * Access-Control-Allow-Methods: POST, PUT, GET, DELETE, HEAD, OPTIONS
+ *
+ * <p/>
+ * Author: Sebastian Schaffert
+ */
+public class LMFOptionsFilter implements Filter {
+
+ @Inject
+ private Logger log;
+
+ @Inject
+ private ConfigurationService configurationService;
+
+ /**
+ * Called by the web container to indicate to a filter that it is being placed into
+ * service. The servlet container calls the init method exactly once after instantiating the
+ * filter. The init method must complete successfully before the filter is asked to do any
+ * filtering work. <br><br>
+ * <p/>
+ * The web container cannot place the filter into service if the init method either<br>
+ * 1.Throws a ServletException <br>
+ * 2.Does not return within a time period defined by the web container
+ */
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ ResteasyProviderFactory.getInstance().addExceptionMapper(new OptionsMapper());
+ }
+
+ /**
+ * The <code>doFilter</code> method of the Filter is called by the container
+ * each time a request/response pair is passed through the chain due
+ * to a client request for a resource at the end of the chain. The FilterChain passed in to this
+ * method allows the Filter to pass on the request and response to the next entity in the
+ * chain.<p>
+ * A typical implementation of this method would follow the following pattern:- <br>
+ * 1. Examine the request<br>
+ * 2. Optionally wrap the request object with a custom implementation to
+ * filter content or headers for input filtering <br>
+ * 3. Optionally wrap the response object with a custom implementation to
+ * filter content or headers for output filtering <br>
+ * 4. a) <strong>Either</strong> invoke the next entity in the chain using the FilterChain object (<code>chain.doFilter()</code>), <br>
+ * * 4. b) <strong>or</strong> not pass on the request/response pair to the next entity in the filter chain to block the request processing<br>
+ * * 5. Directly set headers on the response after invocation of the next entity in the filter chain.
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+
+
+ try {
+
+ chain.doFilter(request,response);
+// DG: The addition of the response header fails because the respons is already committed - so remote the code
+//
+/* if(response instanceof HttpServletResponse) {
+ HttpServletResponse resp = (HttpServletResponse)response;
+ HttpServletRequest req = (HttpServletRequest)request;
+
+ // adjust the header only when the request is not yet com
+ if (! resp.isCommitted() ) {
+ if(!"OPTIONS".equalsIgnoreCase(req.getMethod()) && !resp.containsHeader("Access-Control-Allow-Origin")) {
+ resp.setHeader("Access-Control-Allow-Origin",configurationService.getStringConfiguration("kiwi.allow_origin","*"));
+ }
+ }
+
+ }
+*/
+ } catch (DefaultOptionsMethodException ex) {
+ if(response instanceof HttpServletResponse) {
+ HttpServletResponse resp = (HttpServletResponse)response;
+ HttpServletRequest req = (HttpServletRequest)request;
+
+ if(req.getMethod().equalsIgnoreCase("OPTIONS")) {
+ resp.setStatus(200);
+ resp.resetBuffer();
+
+
+ if(!resp.containsHeader("Access-Control-Allow-Origin")) {
+ resp.setHeader("Access-Control-Allow-Origin",configurationService.getStringConfiguration("kiwi.allow_origin","*"));
+ }
+ if(!resp.containsHeader("Access-Control-Allow-Methods")) {
+ for(String method : configurationService.getListConfiguration("kiwi.allow_methods", Arrays.asList("POST, PUT, GET, DELETE, HEAD".split(",")))) {
+ resp.addHeader("Access-Control-Allow-Methods",method);
+ }
+ }
+ if(req.getHeader("Access-Control-Request-Headers") != null) {
+ String header = req.getHeader("Access-Control-Request-Headers");
+ String[] values = header.split(",");
+ for(String value : values) {
+ resp.addHeader("Access-Control-Allow-Headers",value.trim());
+ }
+ }
+
+ }
+ }
+ }
+
+
+
+ }
+
+ /**
+ * Called by the web container to indicate to a filter that it is being taken out of service. This
+ * method is only called once all threads within the filter's doFilter method have exited or after
+ * a timeout period has passed. After the web container calls this method, it will not call the
+ * doFilter method again on this instance of the filter. <br><br>
+ * <p/>
+ * This method gives the filter an opportunity to clean up any resources that are being held (for
+ * example, memory, file handles, threads) and make sure that any persistent state is synchronized
+ * with the filter's current state in memory.
+ */
+ @Override
+ public void destroy() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Provider
+ protected static class OptionsMapper implements ExceptionMapper<DefaultOptionsMethodException> {
+
+ @Override
+ public Response toResponse(DefaultOptionsMethodException exception) {
+ throw exception;
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/lmf-core/src/main/java/kiwi/core/startup/LMFStartupService.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/startup/LMFStartupService.java b/lmf-core/src/main/java/kiwi/core/startup/LMFStartupService.java
new file mode 100644
index 0000000..929081f
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/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 kiwi.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 kiwi.core.api.config.ConfigurationService;
+import kiwi.core.api.modules.ModuleService;
+import kiwi.core.api.triplestore.SesameService;
+import kiwi.core.api.ui.LMFSystrayLink;
+import kiwi.core.api.user.UserService;
+import kiwi.core.events.SesameStartupEvent;
+import kiwi.core.events.SystemStartupEvent;
+import kiwi.core.model.module.ModuleConfiguration;
+import kiwi.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/c32963d5/lmf-core/src/main/java/kiwi/core/util/CdiUtils.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/util/CdiUtils.java b/lmf-core/src/main/java/kiwi/core/util/CdiUtils.java
new file mode 100644
index 0000000..d45a42e
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/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 kiwi.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/c32963d5/lmf-core/src/main/java/kiwi/core/util/DateUtil.java
----------------------------------------------------------------------
diff --git a/lmf-core/src/main/java/kiwi/core/util/DateUtil.java b/lmf-core/src/main/java/kiwi/core/util/DateUtil.java
new file mode 100644
index 0000000..4762894
--- /dev/null
+++ b/lmf-core/src/main/java/kiwi/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 kiwi.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();
+ }
+
+}