QueryBuilderImpl.java 13.3 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.Binary
 *  javax.jcr.Node
 *  javax.jcr.Property
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  javax.jcr.ValueFactory
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Properties
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.jackrabbit.util.Text
 *  org.apache.sling.api.resource.ResourceResolver
 *  org.apache.sling.commons.osgi.OsgiUtil
 *  org.apache.sling.jcr.resource.JcrResourceResolverFactory
 *  org.apache.sling.jcr.resource.JcrResourceUtil
 *  org.osgi.framework.BundleContext
 *  org.osgi.framework.InvalidSyntaxException
 *  org.osgi.framework.ServiceReference
 *  org.osgi.service.component.ComponentContext
 *  org.osgi.service.component.ComponentFactory
 *  org.osgi.service.component.ComponentInstance
 *  org.osgi.util.tracker.ServiceTracker
 *  org.osgi.util.tracker.ServiceTrackerCustomizer
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.search.impl.builder;

import com.day.cq.search.PredicateConverter;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.eval.PredicateEvaluator;
import com.day.cq.search.impl.builder.FacetCache;
import com.day.cq.search.impl.builder.QueryImpl;
import com.day.cq.search.impl.builder.RootEvaluator;
import com.day.cq.search.writer.ResultHitWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.jcr.Binary;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFactory;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.util.Text;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.apache.sling.jcr.resource.JcrResourceResolverFactory;
import org.apache.sling.jcr.resource.JcrResourceUtil;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.ComponentFactory;
import org.osgi.service.component.ComponentInstance;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(label="Day CQ Query Builder", description="Advanced Query Builder for JCR", metatype=1)
@Service(value={QueryBuilder.class})
@org.apache.felix.scr.annotations.Properties(value={@org.apache.felix.scr.annotations.Property(name="service.description", value={"Day CQ QueryBuilder"})})
public class QueryBuilderImpl
implements QueryBuilder {
    private static final Logger log = LoggerFactory.getLogger(QueryBuilderImpl.class);
    private static final String PROPERTIES_ENCODING = "ISO-8859-1";
    @org.apache.felix.scr.annotations.Property(value={"text", "jcr:description"}, label="Excerpt properties", description="List of properties that will be included in an excerpt")
    private static final String EXCERPT_PROPERTIES = "excerpt.properties";
    private Map<Object, ComponentInstance> instanceMap = new HashMap<Object, ComponentInstance>();
    @org.apache.felix.scr.annotations.Property(intValue={0}, label="Facet cache entries", description="Maximum number of entries to keep in the facet cache. Set to zero to disable caching. Security warning: the cache is shared by all users!")
    private static final String CACHE_MAX_ENTRIES_PROP = "cache.max.entries";
    private static final int DEFAULT_MAX_ENTRIES = 0;
    private static ConcurrentHashMap<Object, ComponentFactory> predicateFactoryCache = new ConcurrentHashMap();
    String[] regexPatterns = new String[]{PredicateEvaluator.class.getName() + "(.*)", ResultHitWriter.class.getName() + "(.*)"};
    @org.apache.felix.scr.annotations.Property(intValue={300}, label="Facet cache lifetime", description="How long (in seconds) to keep facets in the cache. Set to zero to disable automatic expiration.")
    private static final String CACHE_ENTRY_LIFETIME_PROP = "cache.entry.lifetime";
    private static final int DEFAULT_ENTRY_LIFETIME = 300;
    private FacetCache facetCache = new FacetCache(0, 0);
    CustomizedServiceTracker cst = null;
    @Reference
    private JcrResourceResolverFactory jcrResourceResolverFactory;
    private BundleContext bundleContext;
    private Set<String> excerptProperties;

    @Override
    public Query createQuery(Session session) {
        return this.createQuery(new PredicateGroup(), session);
    }

    @Override
    public Query createQuery(PredicateGroup predicates, Session session) {
        return new QueryImpl(predicates, session, this);
    }

    @Override
    public Query loadQuery(String path, Session session) throws RepositoryException, IOException {
        String dataPath = path.substring(1);
        if (!session.getRootNode().hasProperty(dataPath)) {
            dataPath = path.substring(1) + "/" + "jcr:content" + "/" + "jcr:data";
            if (!session.getRootNode().hasProperty(dataPath)) {
                return null;
            }
        }
        Property data = session.getRootNode().getProperty(dataPath);
        Properties props = new Properties();
        if (data.getType() == 1) {
            props.load(new ByteArrayInputStream(data.getString().getBytes("ISO-8859-1")));
        } else {
            props.load(data.getBinary().getStream());
        }
        PredicateGroup root = PredicateConverter.createPredicates(props);
        return this.createQuery(root, session);
    }

    @Override
    public void storeQuery(Query query, String path, boolean createFile, Session session) throws RepositoryException, IOException {
        Properties props = new Properties();
        props.putAll(PredicateConverter.createMap(query.getPredicates()));
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        props.store(out, null);
        if (createFile) {
            Node node = JcrResourceUtil.createPath((String)path, (String)"nt:unstructured", (String)"nt:file", (Session)session, (boolean)false);
            if (!node.isNodeType("nt:file")) {
                throw new RepositoryException("The path for saving the query already exists and is not of type nt:file: '" + path + "'");
            }
            node = node.hasNode("jcr:content") ? node.getNode("jcr:content") : node.addNode("jcr:content", "nt:resource");
            node.setProperty("jcr:mimeType", "text/plain");
            node.setProperty("jcr:encoding", "ISO-8859-1");
            node.setProperty("jcr:data", session.getValueFactory().createBinary((InputStream)new ByteArrayInputStream(out.toByteArray())));
            node.setProperty("jcr:lastModified", Calendar.getInstance());
        } else {
            String nodePath = Text.getRelativeParent((String)path, (int)1);
            String property = Text.getName((String)path);
            Node node = JcrResourceUtil.createPath((String)nodePath, (String)"nt:unstructured", (String)"nt:unstructured", (Session)session, (boolean)false);
            node.setProperty(property, new String(out.toByteArray(), "ISO-8859-1"));
        }
        session.save();
    }

    public ResourceResolver createResourceResolver(Session session) {
        return this.jcrResourceResolverFactory.getResourceResolver(session);
    }

    public Set<String> getExcerptPropertyNames() {
        return this.excerptProperties;
    }

    public RootEvaluator createRootEvaluator() {
        return new RootEvaluator();
    }

    protected void activate(ComponentContext context) {
        String[] excerptProps;
        this.bundleContext = context.getBundleContext();
        this.cst = new CustomizedServiceTracker(this.bundleContext, ComponentFactory.class.getName(), null);
        this.cst.open();
        this.excerptProperties = new HashSet<String>();
        for (String prop : excerptProps = (String[])context.getProperties().get("excerpt.properties")) {
            if (prop == null || prop.trim().length() <= 0) continue;
            this.excerptProperties.add(prop);
        }
        int maxEntries = OsgiUtil.toInteger(context.getProperties().get("cache.max.entries"), (int)0);
        int entryLifetimeMsec = 1000 * OsgiUtil.toInteger(context.getProperties().get("cache.entry.lifetime"), (int)300);
        this.facetCache = new FacetCache(maxEntries, entryLifetimeMsec);
    }

    protected void deactivate(ComponentContext context) {
        this.bundleContext = null;
        this.excerptProperties = null;
        this.facetCache = null;
        this.cst.close();
    }

    @Override
    public void clearFacetCache() {
        this.facetCache.clear();
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public <ComponentT> ComponentT getComponent(Class<ComponentT> typeClass, String type, Dictionary properties) {
        Object component;
        ComponentInstance instance;
        ComponentFactory factory = predicateFactoryCache.get(typeClass.getName() + '/' + type);
        if (factory == null) {
            log.error("Unable to get " + typeClass.getSimpleName() + " instance: " + type);
            ServiceReference[] refs = null;
            try {
                refs = this.bundleContext.getServiceReferences(ComponentFactory.class.getName(), "(component.factory=" + typeClass.getName() + '/' + type + ')');
            }
            catch (InvalidSyntaxException e) {
                log.error("Unable to get " + typeClass.getSimpleName() + " instance: " + type, (Throwable)e);
                return null;
            }
            if (refs == null || refs.length == 0) {
                return null;
            }
            ServiceReference ref = refs[0];
            factory = (ComponentFactory)this.bundleContext.getService(ref);
        }
        if ((component = (instance = factory.newInstance(properties)).getInstance()) == null) {
            log.error("Unable to get " + typeClass.getSimpleName() + " instance: " + type);
            instance.dispose();
        } else {
            Map<Object, ComponentInstance> map = this.instanceMap;
            synchronized (map) {
                this.instanceMap.put(component, instance);
            }
        }
        return (ComponentT)component;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void releaseComponent(Object component) {
        Map<Object, ComponentInstance> map = this.instanceMap;
        synchronized (map) {
            if (this.instanceMap.containsKey(component)) {
                this.instanceMap.get(component).dispose();
                this.instanceMap.remove(component);
            }
        }
    }

    public FacetCache getFacetCache() {
        if (this.facetCache == null) {
            throw new IllegalStateException("FacetCache is null, not activated?");
        }
        return this.facetCache;
    }

    protected void bindJcrResourceResolverFactory(JcrResourceResolverFactory jcrResourceResolverFactory) {
        this.jcrResourceResolverFactory = jcrResourceResolverFactory;
    }

    protected void unbindJcrResourceResolverFactory(JcrResourceResolverFactory jcrResourceResolverFactory) {
        if (this.jcrResourceResolverFactory == jcrResourceResolverFactory) {
            this.jcrResourceResolverFactory = null;
        }
    }

    private class CustomizedServiceTracker
    extends ServiceTracker {
        public CustomizedServiceTracker(BundleContext context, String clazz, ServiceTrackerCustomizer customizer) {
            super(context, clazz, customizer);
        }

        public Object addingService(ServiceReference reference) {
            String componentFactoryName = (String)reference.getProperty("component.factory");
            boolean matched = false;
            for (String pattern : QueryBuilderImpl.this.regexPatterns) {
                if (!componentFactoryName.matches(pattern)) continue;
                matched = true;
            }
            if (matched) {
                predicateFactoryCache.put(componentFactoryName, (ComponentFactory)QueryBuilderImpl.this.bundleContext.getService(reference));
                log.debug("Adding component: " + componentFactoryName);
            }
            return super.addingService(reference);
        }

        public void removedService(ServiceReference reference, Object service) {
            String componentFactoryName = (String)reference.getProperty("component.factory");
            boolean matched = false;
            for (String pattern : QueryBuilderImpl.this.regexPatterns) {
                if (!componentFactoryName.matches(pattern)) continue;
                matched = true;
            }
            if (matched) {
                predicateFactoryCache.remove(componentFactoryName);
                log.debug("Removing Component: " + componentFactoryName);
            }
            super.removedService(reference, service);
        }
    }

}