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) */ 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 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.apache.log4j.BasicConfigurator; import org.apache.log4j.Logger; 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(BaseRestlet.class); /** * Properties holding consumer keys and secrets. */ protected Properties consumerKeys; public String CONSUMER_KEYS_PATH = "WEB-INF/consumerkeys.property"; public static final String CONSUMERKEYS_KEY = "annotationmanager.consumerkeys"; /** * Properties holding server config. */ protected Properties serverConfig; public String CONFIG_PROPS_PATH = "WEB-INF/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 = "WEB-INF/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"; public static final String ADMIN_USER_KEY = "annotationmanager.admin.user"; 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"; /** * constructor * * @param context */ public BaseRestlet(Context context) { super(context); configure(); } /** * Configures the restlet. * Reads serverConfig, consumerKeys and graphDb config from config files and starts graphDb. * Uses config from webapp context if already initialized. */ protected void configure() { ServletContext sc = (ServletContext) getContext().getServerDispatcher().getContext().getAttributes() .get("org.restlet.ext.servlet.ServletContext"); if (sc != null) { if (sc.getAttribute("annotationserver.log4j.configured") == null) { // TODO: is this the right place to run the log4j configurator? BasicConfigurator.configure(); sc.setAttribute("annotationserver.log4j.configured", "done"); } 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.debug("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.warn("Error loading server config: ", e); } logger.debug("config: " + serverConfig); } else { logger.error("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.debug("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.debug("Starting DB admin server..."); // store in context sc.setAttribute(GRAPHDBSRV_KEY, srv); srv.start(); } else { logger.error("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.debug("loading consumer keys from " + CONSUMER_KEYS_PATH); try { consumerKeys.load(ps); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } logger.debug("consumer keys: " + consumerKeys); } else { logger.error("Unable to get resource " + CONSUMER_KEYS_PATH); } // store config sc.setAttribute(CONSUMERKEYS_KEY, consumerKeys); } } else { logger.error("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); } /** * 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.error("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.error("Error in getFullNameFromLDAP!", e); } try { dctx.close(); } catch (NamingException e) { logger.error("Error in getFullNameFromLDAP!", e); } return retString; } /** * returns resource from path (in webapp) as InputStream. * * @param sc * @param path * @return */ protected InputStream getResourceAsStream(ServletContext sc, String path) { InputStream ps = sc.getResourceAsStream(path); if (ps == null) { // try as file File pf = new File(sc.getRealPath(path)); if (pf != null) { logger.debug("trying file for: " + pf); try { ps = new FileInputStream(pf); } catch (FileNotFoundException e) { logger.error(e); } } } 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 base directory of 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()) { // relative path -> use getRealPath to resolve in webapp filename = sc.getRealPath(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.debug("Stopping DB admin server..."); srv.stop(); srv = null; } if (graphDb != null) { logger.debug("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(); } }); } }