Package com.hp.hpl.squirrelrdf.ldap

Source Code of com.hp.hpl.squirrelrdf.ldap.LdapSubQuery$SingletonNamingEnumeration

/*
* (c) Copyright 2006 Hewlett-Packard Development Company, LP
* All rights reserved.
* [See end of file]
*/

package com.hp.hpl.squirrelrdf.ldap;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.graph.impl.LiteralLabel;
import com.hp.hpl.jena.query.core.Binding;
import com.hp.hpl.jena.query.engine1.ExecutionContext;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.squirrelrdf.querymap.QueryMapEngine;

/**
* The meat of the mapping. Takes a triple pattern (with same subject), maps to
* an ldap query, and maps that result to rdf node values.
*
* @author pldms
*
*/
public class LdapSubQuery implements QueryMapEngine
{
  private static final Log log = LogFactory.getLog(LdapSubQuery.class);

  private Node subject;

  private List<Triple> query;

  private Model config;

  private Resource server;

  private Resource base;
 
  private Node type;
 
  public LdapSubQuery(Model config)
  {
    this.subject = null;
    this.query = new ArrayList<Triple>();
    this.config = config;
    this.base = getSubject(RDF.type, LdapMap.Map).getProperty(
        LdapMap.server).getResource();
    this.server = getServer(base);
    this.type = null;
  }
 
  /* Get the server part from the base */
  private Resource getServer(Resource base2)
  {
    String server = base2.getURI().substring(0, base2.getURI().lastIndexOf("/") + 1);
    return base2.getModel().createResource(server);
  }

  /**
   * Add a triple. All triples should have the same subject.
   *
   * @param triple
   */
  public void add(Triple triple)
  {
    if (subject == null)
      subject = triple.getSubject();
   
    query.add(triple);
  }
 
  /**
   * Get the subject for this pattern.
   * @return The subject
   */
  public Node getSubject()
  {
    return subject;
  }
 
  /**
   * Get the pattern as a list of triples
   * @return Query pattern
   */
  public List<Triple> getTriples()
  {
    return query;
  }

  /*
   * Execute this (sub)query, given an exisiting binding.
   *
   * The bulk of this is concerned with mapping the ldap results to rdf, and
   * checking whether the binding is consistent. Since we prebind this is a
   * little over the top (though cheap). We could also check for cross
   * products and cache results.
   *
   * (non-Javadoc)
   *
   * @see com.hp.hpl.eim.ldap.QueryMapEngine#execute(com.hp.hpl.jena.query.core.Binding)
   */
  public Iterator<Map<String, Node>> execute(Binding binding, ExecutionContext context)
  {
    List<Map<String, Node>> result = new ArrayList<Map<String, Node>>();

    NamingEnumeration<SearchResult> ldapResult = null;

    try
    {
      ldapResult = executeLdap(binding, context);

      while (ldapResult.hasMore())
      {
        SearchResult sr = ldapResult.next();
        Attributes sra = sr.getAttributes();
       
        Map<String, Node> aBinding = new HashMap<String, Node>();

        // Mapping results to rdf
        if (subject.isVariable())
          aBinding.put(subject.getName(), createLdapNode(sr
              .getNameInNamespace()));

        boolean valid = true;

        for (Triple triple : query)
        {
          if (!triple.getObject().isVariable())
            continue;

          String attribute = predToAttribute(triple.getPredicate());

          if (sra.get(attribute) == null) // no value -- not a hit
          {
            log.debug("Attribute not found");
            valid = false;
            break;
          }
         
          // FIXME I get the first value, but there may be more. tricky.
          // TODO If pred is type unmap value to node (often multiple vals, btw)
          Object val = sra.get(attribute).get();
          String var = triple.getObject().getName();

          // I don't think this does anything useful, since we prebind
          if (aBinding.containsKey(var)) // if we have a binding for
          // this
          {
            if (!aBinding.get(var).equals(val)) // and it differs,
            // not a consistent
            // binding
            {
              valid = false;
              break;
            }
            continue; // All is well, keep what we have
          }

          Node nodeVal;
          Resource valType = getObjectType(triple.getPredicate());
          // This attribute points to a ldap node
          if (LdapMap.ObjectProperty.equals(valType))
            nodeVal = createLdapNode((String) val);
          else if (LdapMap.EmailProperty.equals(valType))
            nodeVal = Node.createURI("mailto:" + val);
          else if (LdapMap.URIProperty.equals(valType))
            nodeVal = Node.createURI(val.toString());
          else if (val instanceof String) // avoid xsd:string
            nodeVal = Node.createLiteral((String) val);
          else
          {
            LiteralLabel ll = new LiteralLabel(val);
            nodeVal = Node.createLiteral(ll);
          }
          aBinding.put(var, nodeVal);
        }

        if (valid)
          result.add(aBinding);
      }

      ldapResult.close();
    }
    catch (NamingException e) // Flee, all is lost...
    {
      throw new RuntimeException("Ldap problem", e);
    }

    return result.iterator();
  }

  /**
   *  Create an ldap:// uri node from a basename. Normalise a little.
   * 
   */
  private Node createLdapNode(String val)
  {
    String toReturn = server.getURI()
        + val.toLowerCase().replaceAll(" ", "%20");
    return Node.createURI(toReturn);
  }

  /**
   * Execute the query.
   *
   * This takes a subquery, creates, and executes an ldap query, prebinding
   * variables if possible.
   *
   * @param binding
   * @return LDAP result
   * @throws NamingException
   */
  public NamingEnumeration<SearchResult> executeLdap(Binding binding, ExecutionContext econtext)
      throws NamingException
  {
    if (query.isEmpty())
    {
      log.warn("(sub)Query is empty");
      return null;
    }

    Collection<String> toGet = new ArrayList<String>();
    Collection<String> toMatch = new ArrayList<String>();

    /* Prebind subject (if possible) */
    Node theSubject = subject;
    if (subject.isVariable() && binding.contains(subject.getName()))
      theSubject = binding.get(subject.getName());

    for (Triple triple : query)
    {
      String attr = predToAttribute(triple.getPredicate());

      /* Prebind object (if possible) */
      Node theObject = triple.getObject();
      if (theObject.isVariable() && binding.contains(theObject.getName()))
        theObject = binding.get(theObject.getName());

      Resource valType = getObjectType(triple.getPredicate());
      if (theObject.isVariable())
        toGet.add(attr);
      else // TODO if pred is type, map values to ldap equivalent
      {
        toGet.add(attr); /* TODO dump this! stupid! Prebinding work around */
        String match = null;

        if (LdapMap.ObjectProperty.equals(valType))
          match = unldap(theObject.getURI());
        else if (LdapMap.EmailProperty.equals(valType))
          match = unmailto(theObject.getURI());
        else if (theObject.isURI()) // This could occur even if type
          // isn't given
          match = theObject.getURI();
        else
          match = theObject.getLiteralLexicalForm();

        log.info("attr: '" + attr + "' match: '" + match + "'");

        /* TODO Escape 'match' properly */
        toMatch.add("(" + attr + "=" + match + ")");
      }
    }

    String queryString;

    if (toMatch.size() > 1)
      queryString = "(&" + join(toMatch, "") + ")";
    else
      queryString = join(toMatch, "");

    String baseURI = null;
   
    if (theSubject.isVariable())
      baseURI = base.getURI();
    else
    {
      if (!theSubject.isURI()
          || !theSubject.getURI().startsWith(server.getURI()))
      {
        throw new RuntimeException(
            "Unanswerable query. Subject invalid: " + theSubject);
      }

      baseURI = theSubject.getURI();
    }
   
    log.info("Server: " + baseURI);
    log.info("filter: " + queryString);
    log.info("get attributes: " + join(toGet, ","));
   

    DirContext context = new InitialDirContext();
   
    NamingEnumeration<SearchResult> results = null;
   
    if ("".equals(queryString)) // We aren't searching, just get attributes
    {
      Attributes attrs = context.getAttributes(baseURI, (String[]) toGet
          .toArray(new String[] {}));
      // Wrap this to fit this method's contract
      SearchResult sr = new SearchResult(null, null, attrs);
      sr.setNameInNamespace(uriToName(baseURI));
      results = new SingletonNamingEnumeration<SearchResult>(sr);
    }
    else // An actual query
    {
      SearchControls sc = new SearchControls();
      sc.setReturningAttributes(toGet.toArray(new String[] {}));
      sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
     
      results = context.search(baseURI, queryString, sc);
    }

    return results;
  }

  private String unmailto(String uri)
  {
    if (uri.startsWith("mailto:"))
      return uri.substring(7);

    return uri;
  }

  private String unldap(String uri)
  {
    if (uri.startsWith(server.getURI()))
      return uri.substring(server.getURI().length()).replaceAll("%20",
          " ");

    return uri;
  }

  private String uriToName(String uri)
  {
    return uri.substring(server.getURI().length());
  }

  private String join(Collection<String> toJoin, String joinString)
  {
    StringBuffer sb = new StringBuffer();
    String js = null;
    for (String joinElem : toJoin)
    {
      if (js != null)
        sb.append(js);
      sb.append(joinElem);
      if (js == null)
        js = joinString;
    }
    return sb.toString();
  }

  private String predToAttribute(Node predicate)
  {
    return getSubject(LdapMap.property, config.asRDFNode(predicate))
        .getProperty(LdapMap.attribute).getLiteral().getString();
  }

  private Resource getSubject(Property property, RDFNode object)
  {
    ResIterator found = config.listSubjectsWithProperty(property, object);
    if (!found.hasNext())
      return null;
    Resource foundSubject = found.nextResource();
    found.close();
    return foundSubject;
  }

  private Resource getObjectType(Node predicate)
  {
    Resource subj = getSubject(LdapMap.property, config
        .asRDFNode(predicate));
    if (subj == null)
      return null;
    Statement s = subj.getProperty(RDF.type);
    if (s == null)
      return null;
    return s.getResource();
  }

  /**
   *
   * This is a simple implementation to keep executeLdap uniform. It is for
   * cases where no search is perfomed.
   *
   * @author pldms
   *
   */
  class SingletonNamingEnumeration<T> implements NamingEnumeration<T>
  {
    boolean hasMore;

    T element;

    public SingletonNamingEnumeration(T element)
    {
      this.element = element;
      hasMore = true;
    }

    public T next() throws NamingException
    {
      return nextElement();
    }

    public boolean hasMore() throws NamingException
    {
      return hasMore;
    }

    public void close() throws NamingException
    {
    }

    public boolean hasMoreElements()
    {
      return hasMore;
    }

    public T nextElement()
    {
      if (hasMore)
      {
        hasMore = false;
        return element;
      }

      return null;
    }

  }
}


/*
* (c) Copyright 2006 Hewlett-Packard Development Company, LP All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. The name of the author may not
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
TOP

Related Classes of com.hp.hpl.squirrelrdf.ldap.LdapSubQuery$SingletonNamingEnumeration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.