package de.mpiwg.itgroup.annotations.restlet; /* * #%L * AnnotationManager * %% * Copyright (C) 2012 - 2014 MPIWG Berlin * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% * Author: Robert Casties (casties@mpiwg-berlin.mpg.de), * Dirk Wintergruen (dwinter@mpiwg-berlin.mpg.de) */ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Hashtable; import java.util.Properties; import java.util.concurrent.ConcurrentMap; import java.util.logging.Logger; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.servlet.ServletContext; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.factory.GraphDatabaseBuilder; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.kernel.GraphDatabaseAPI; import org.neo4j.server.WrappingNeoServerBootstrapper; import org.restlet.Application; import org.restlet.Context; import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore; public abstract class BaseRestlet extends Application { public static Logger logger = Logger.getLogger("de.mpiwg.itgroup.annotations.restlet.BaseRestlet"); /** * Properties holding consumer keys and secrets. */ protected Properties consumerKeys; public String CONSUMER_KEYS_PATH = "consumerkeys.property"; public static final String CONSUMERKEYS_KEY = "annotationmanager.consumerkeys"; /** * Properties holding server config. */ protected Properties serverConfig; public String CONFIG_PROPS_PATH = "serverconfig.property"; public static final String SERVERCONFIG_KEY = "annotationmanager.serverconfig"; /** * database instance; */ protected GraphDatabaseService graphDb; public static final String GRAPHDB_KEY = "annotationmanager.graphdb"; public static final String GRAPHDB_PATH_KEY = "annotationmanager.graphdb.path"; public String graphdbPath = "neo4j-annotation-db"; /** * database interface server instance. */ protected WrappingNeoServerBootstrapper srv; public static final String GRAPHDBSRV_KEY = "annotationmanager.graphdb.srv"; /** * annotation store instance. */ protected AnnotationStore store; public static final String ANNSTORE_KEY = "annotationmanager.store"; /** * LDAP server URI (for looking up full user names). */ protected String ldapServerUrl; public static final String LDAP_SERVER_KEY = "annotationmanager.ldapserver.url"; /** * web frontend admin user name */ public static final String ADMIN_USER_KEY = "annotationmanager.admin.user"; /** * web frontend admin user password */ public static final String ADMIN_PASSWORD_KEY = "annotationmanager.admin.password"; /** * run in authorization mode i.e. with tokens. */ protected boolean authorizationMode = false; public static final String AUTHORIZATION_MODE_KEY = "annotationmanager.authorization"; /** * prefix to create uris for tags in store. */ public static String TAGS_URI_PREFIX = ""; public static final String TAGS_URI_KEY = "annotationmanager.uris.tags"; /** * prefix to create uris for persons in store. */ public static String PERSONS_URI_PREFIX = ""; public static final String PERSONS_URI_KEY = "annotationmanager.uris.persons"; /** * prefix to create uris for groups in store. */ public static String GROUPS_URI_PREFIX = ""; public static final String GROUPS_URI_KEY = "annotationmanager.uris.groups"; public static final String ANNOTATIONS_URI_KEY = "annotationmanager.uris.annotations"; /* (non-Javadoc) * @see org.restlet.Application#start() */ @Override public synchronized void start() throws Exception { configure(getContext()); super.start(); } /** * Configures the restlet. * Reads serverConfig, consumerKeys and graphDb config from config files and starts graphDb. * Uses config from webapp context if already initialized. * @param context */ protected void configure(Context context) { ConcurrentMap attrs = context.getAttributes(); ServletContext sc = (ServletContext) attrs.get("org.restlet.ext.servlet.ServletContext"); if (sc != null) { logger = context.getLogger(); logger.info(getVersion() + " starting..."); /* * read config from webapp */ serverConfig = (Properties) sc.getAttribute(SERVERCONFIG_KEY); if (serverConfig == null) { serverConfig = new Properties(); InputStream ps = getResourceAsStream(sc, CONFIG_PROPS_PATH); if (ps != null) { logger.fine("loading config from " + CONFIG_PROPS_PATH); try { serverConfig.load(ps); /* * read serverconfig options */ graphdbPath = serverConfig.getProperty(GRAPHDB_PATH_KEY, graphdbPath); ldapServerUrl = serverConfig.getProperty(LDAP_SERVER_KEY, null); /* * uri prefixes */ if (serverConfig.containsKey(PERSONS_URI_KEY)) { BaseRestlet.PERSONS_URI_PREFIX = serverConfig.getProperty(PERSONS_URI_KEY); } if (serverConfig.containsKey(GROUPS_URI_KEY)) { BaseRestlet.GROUPS_URI_PREFIX = serverConfig.getProperty(GROUPS_URI_KEY); } if (serverConfig.containsKey(TAGS_URI_KEY)) { BaseRestlet.TAGS_URI_PREFIX = serverConfig.getProperty(TAGS_URI_KEY); } if (serverConfig.containsKey(ANNOTATIONS_URI_KEY)) { AnnotationStore.ANNOTATION_URI_PREFIX = serverConfig.getProperty(ANNOTATIONS_URI_KEY); } } catch (IOException e) { logger.warning("Error loading server config: "+e.toString()); } logger.fine("config: " + serverConfig); } else { logger.severe("Unable to get resource " + CONFIG_PROPS_PATH); } // store config sc.setAttribute(SERVERCONFIG_KEY, serverConfig); } // look for database service in context graphDb = (GraphDatabaseService) sc.getAttribute(GRAPHDB_KEY); if (graphDb == null) { /* * open database */ String dbFn = getResourcePath(sc, graphdbPath); if (dbFn != null) { logger.fine("opening DB " + dbFn); GraphDatabaseBuilder graphDbBuilder = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder(dbFn); graphDbBuilder.setConfig(GraphDatabaseSettings.allow_store_upgrade, "true"); graphDb = graphDbBuilder.newGraphDatabase(); registerShutdownHook(graphDb); // store in context sc.setAttribute(GRAPHDB_KEY, graphDb); // AnnotationStore store = new AnnotationStore(graphDb); sc.setAttribute(ANNSTORE_KEY, store); // admin server srv = new WrappingNeoServerBootstrapper((GraphDatabaseAPI) graphDb); logger.fine("Starting DB admin server..."); // store in context sc.setAttribute(GRAPHDBSRV_KEY, srv); srv.start(); } else { logger.severe("Unable to get resource " + dbFn); } } else { // get existing AnnotationStore store = (AnnotationStore) sc.getAttribute(ANNSTORE_KEY); } /* * read consumerKeys from webapp */ consumerKeys = (Properties) sc.getAttribute(CONSUMERKEYS_KEY); if (consumerKeys == null) { consumerKeys = new Properties(); InputStream ps = getResourceAsStream(sc, CONSUMER_KEYS_PATH); if (ps != null) { logger.fine("loading consumer keys from " + CONSUMER_KEYS_PATH); try { consumerKeys.load(ps); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } logger.fine("consumer keys: " + consumerKeys); } else { logger.severe("Unable to get resource " + CONSUMER_KEYS_PATH); } // store config sc.setAttribute(CONSUMERKEYS_KEY, consumerKeys); } } else { logger.severe("Unable to get ServletContext!"); } } public abstract String getVersion(); /** * @return the authorizationMode */ public boolean isAuthorizationMode() { return authorizationMode; } /** * @return the store */ public AnnotationStore getAnnotationStore() { return store; } /** * returns consumer secret for consumer key. returns null if consumer key * doesn't exist. * * @param consumerKey * @return */ public String getConsumerSecret(String consumerKey) { return consumerKeys.getProperty(consumerKey); } /** * Return the full name (String) of the person with the given user-id. * * Contacts a naming service (currently LDAP). * * @param userId * @return full-name */ public String getFullNameForId(String userId) { return getFullNameFromLdap(userId); } /** * Hole den vollen Benutzernamen aus dem LDAP * * @param creator * @return */ public String getFullNameFromLdap(String creator) { String retString = creator; // falls nichts gefunden wird einfach den // creator zurueckgeben if (ldapServerUrl == null) { return retString; } Hashtable env = new Hashtable(); String sp = "com.sun.jndi.ldap.LdapCtxFactory"; env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, sp); env.put(javax.naming.Context.PROVIDER_URL, ldapServerUrl); DirContext dctx; try { dctx = new InitialDirContext(env); } catch (NamingException e) { logger.warning("Error in getFullNameFromLDAP! "+e); return retString; } String base = "ou=people"; SearchControls sc = new SearchControls(); String[] attributeFilter = { "cn", "mail" }; sc.setReturningAttributes(attributeFilter); sc.setSearchScope(SearchControls.SUBTREE_SCOPE); String filter = "(uid=" + creator + ")"; try { NamingEnumeration results = dctx.search(base, filter, sc); while (results.hasMore()) { SearchResult sr = (SearchResult) results.next(); javax.naming.directory.Attributes attrs = sr.getAttributes(); Attribute attr = attrs.get("cn"); retString = (String) attr.get(); } } catch (NamingException e) { logger.warning("Error in getFullNameFromLDAP!"+e); } try { dctx.close(); } catch (NamingException e) { logger.warning("Error in getFullNameFromLDAP!"+e); } return retString; } /** * returns resource from path as InputStream. * * Tries path in webapp first, then uses classpath loader. * Relative paths in webapp start in /WEB-INF/. * * @param sc * @param path * @return */ protected InputStream getResourceAsStream(ServletContext sc, String path) { InputStream ps = null; if (sc == null) { // no servlet context -> use class loader ps = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); } else { // try path in webapp first String webPath = path; if (!webPath.startsWith("/")) { // relative path in webapp starts in WEB-INF webPath = "/WEB-INF/" + webPath; } ps = sc.getResourceAsStream(webPath); if (ps == null) { // try as file File pf = new File(sc.getRealPath(webPath)); if (pf.canRead()) { logger.fine("trying file for: " + pf); try { ps = new FileInputStream(pf); } catch (FileNotFoundException e) { logger.severe(e.toString()); } } else { // use class loader ps = Thread.currentThread().getContextClassLoader().getResourceAsStream(path); } } } return ps; } /** * get a real file name for a web app file pathname. * * If filename starts with "/" its treated as absolute else the path is * appended to the /WEB-INF/ directory in the web-app. * * @param filename * @param sc * @return */ public static String getResourcePath(ServletContext sc, String filename) { File f = new File(filename); // is the filename absolute? if (!f.isAbsolute() && sc != null) { // relative path -> use getRealPath to resolve in webapp filename = sc.getRealPath("/WEB-INF/" + filename); } return filename; } /* * (non-Javadoc) * * @see org.restlet.Application#stop() */ @Override public synchronized void stop() throws Exception { /* * trying to clean up databases, not sure if this is the right way... */ if (srv != null) { logger.info("Stopping DB admin server..."); srv.stop(); srv = null; } if (graphDb != null) { logger.info("Stopping DB..."); graphDb.shutdown(); graphDb = null; } super.stop(); } private static void registerShutdownHook(final GraphDatabaseService graphDb) { // Registers a shutdown hook for the Neo4j instance so that it // shuts down nicely when the VM exits (even if you "Ctrl-C" the // running example before it's completed) Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { graphDb.shutdown(); } }); } }