Package com.trsst.server

Source Code of com.trsst.server.AbderaProvider$OrderedRegexTargetResolver

/*
s * Copyright 2013 mpowers
*
* 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 com.trsst.server;

import java.io.IOException;
import java.io.StringReader;
import java.net.InetAddress;
import java.util.Collection;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.security.auth.Subject;

import org.apache.abdera.Abdera;
import org.apache.abdera.model.Feed;
import org.apache.abdera.model.Workspace;
import org.apache.abdera.parser.ParseException;
import org.apache.abdera.parser.Parser;
import org.apache.abdera.protocol.Request;
import org.apache.abdera.protocol.server.CollectionAdapter;
import org.apache.abdera.protocol.server.CollectionInfo;
import org.apache.abdera.protocol.server.Filter;
import org.apache.abdera.protocol.server.FilterChain;
import org.apache.abdera.protocol.server.ProviderHelper;
import org.apache.abdera.protocol.server.RequestContext;
import org.apache.abdera.protocol.server.RequestProcessor;
import org.apache.abdera.protocol.server.ResponseContext;
import org.apache.abdera.protocol.server.Target;
import org.apache.abdera.protocol.server.TargetType;
import org.apache.abdera.protocol.server.Transactional;
import org.apache.abdera.protocol.server.WorkspaceInfo;
import org.apache.abdera.protocol.server.WorkspaceManager;
import org.apache.abdera.protocol.server.context.RequestContextWrapper;
import org.apache.abdera.protocol.server.context.ResponseContextException;
import org.apache.abdera.protocol.server.filters.OpenSearchFilter;
import org.apache.abdera.protocol.server.impl.AbstractWorkspaceProvider;
import org.apache.abdera.protocol.server.impl.RegexTargetResolver;
import org.apache.abdera.protocol.server.impl.SimpleCollectionInfo;
import org.apache.abdera.protocol.server.impl.TemplateTargetBuilder;

import com.trsst.Common;

/**
* Abdera-specific configuration of mapping targets and servlet filters.
*
* @author mpowers
*/
public class AbderaProvider extends AbstractWorkspaceProvider implements
        WorkspaceInfo {

    Hashtable<String, TrsstAdapter> idsToAdapters = new Hashtable<String, TrsstAdapter>();
    String hostname;

    public AbderaProvider() {
        try {
            hostname = InetAddress.getLocalHost().getHostName();
            getStorage(); // force storage to load if necessary
        } catch (Throwable t) {
            log.info("Could not obtain hostname: defaulting to 'localhost'");
            hostname = "localhost";
        }
    }

    @Override
    public void init(Abdera abdera, Map<String, String> properties) {
        // can receive servlet init params here
        super.init(abdera, properties);

        // map paths to handlers
        RegexTargetResolver resolver = new OrderedRegexTargetResolver();
        resolver.setPattern("/service", TargetType.TYPE_SERVICE)
                .setPattern("/(http[^#?]*)/([0-9a-fA-F]{11})",
                        TargetType.TYPE_ENTRY, "collection", "entry")
                // external entry
                .setPattern("/(http[^#?]*)", TargetType.TYPE_COLLECTION,
                        "collection")
                // external feed
                .setPattern("/([^/#?]+)/([^/#?]+)/([^/#?]+)(\\?[^#]*)?",
                        TargetType.TYPE_MEDIA, "collection", "entry",
                        "resource")
                .setPattern("/([^/#?]+)/([^/#?]+)(\\?[^#]*)?",
                        TargetType.TYPE_ENTRY, "collection", "entry")
                .setPattern("/([^/#?]+);categories",
                        TargetType.TYPE_CATEGORIES, "collection")
                .setPattern("/([^/#?;]+)(\\?[^#]*)?",
                        TargetType.TYPE_COLLECTION, "collection")
                .setPattern("/", TargetType.TYPE_COLLECTION);

        super.setTargetResolver(resolver);

        // url construction templates
        setTargetBuilder(new TemplateTargetBuilder()
                .setTemplate(TargetType.TYPE_SERVICE, "{target_base}")
                .setTemplate(TargetType.TYPE_COLLECTION,
                        "{target_base}/{collection}{-opt|?|q,c,s,p,l,i,o}{-join|&|q,c,s,p,l,i,o}")
                .setTemplate(TargetType.TYPE_CATEGORIES,
                        "{target_base}/{collection};categories")
                .setTemplate(TargetType.TYPE_ENTRY,
                        "{target_base}/{collection}/{entry}"));

        addWorkspace(this);
        addFilter(new PaginationFilter());
        addFilter(new OpenSearchFilter()
                .setShortName("Trsst Search")
                .setDescription("Search on entry metadata.")
                .setTags("test", "example", "opensearch")
                .setContact("admin@trsst.com")
                .setTemplate(
                        "{target_base}/?q={searchTerms}&count={count?}&page={startPage?}&offset={startIndex?}&before={beforeDate?}&after={afterDate?}")
                // .setTemplate(
                // "{target_base}/?q={searchTerms}&c={count?}&s={startIndex?}&p={startPage?}&l={language?}&i={indexEncoding?}&o={outputEncoding?}")
                .mapTargetParameter("q", "searchTerms")
                .mapTargetParameter("count", "count")
                .mapTargetParameter("before", "beforeDate")
                .mapTargetParameter("after", "afterDates")
                .mapTargetParameter("page", "startPage")
                .mapTargetParameter("offset", "startIndex"));
        // .mapTargetParameter("l", "language")
        // .mapTargetParameter("i", "inputEncoding")
        // .mapTargetParameter("o", "outputEncoding"));

    }

    public ResponseContext process(RequestContext request) {
        Target target = request.getTarget();
        if (target == null || target.getType() == TargetType.TYPE_NOT_FOUND) {
            return ProviderHelper.notfound(request);
        }

        TargetType type = target.getType();
        RequestProcessor processor = this.requestProcessors.get(type);
        if (processor == null) {
            return ProviderHelper.notfound(request);
        }

        WorkspaceManager wm = getWorkspaceManager(request);
        CollectionAdapter adapter = wm.getCollectionAdapter(request);
        Transactional transaction = adapter instanceof Transactional ? (Transactional) adapter
                : null;
        ResponseContext response = null;
        try {
            transactionStart(transaction, request);
            response = processor.process(request, wm, adapter);
            response = response != null ? response : processExtensionRequest(
                    request, adapter);
        } catch (Throwable e) {
            if (e instanceof ResponseContextException) {
                ResponseContextException rce = (ResponseContextException) e;
                if (rce.getStatusCode() >= 400 && rce.getStatusCode() < 500) {
                    // don't report routine 4xx HTTP errors
                    log.info("info: ", e);
                } else {
                    log.error("inner error: ", e);
                }
            } else {
                log.error("outer error: ", e);
            }
            transactionCompensate(transaction, request, e);
            response = createErrorResponse(request, e);
            return response;
        } finally {
            transactionEnd(transaction, request, response);
        }

        return response != null ? response : ProviderHelper.badrequest(request);
    }

    // @Override
    // public ResponseContext process(RequestContext request) {
    // try {
    // log.info(request.getMethod().toString() + " "
    // + request.getUri().toString());
    // return super.process(request);
    // } catch (Throwable t) {
    // log.info(request.getMethod().toString() + " "
    // + request.getUri().toString());
    // log.error("Unexpected error: " + t);
    // }
    // return null;
    // }
    //
    /**
     * Returns null to function in containers with constrained permissions;
     * Trsst servers generally don't need http security contexts.
     */
    @Override
    public Subject resolveSubject(RequestContext request) {
        // return null to work in containers with constrained permissions
        return null;
    }

    /**
     * Override to return a custom storage instance. This implementation
     * defaults to a single shared LuceneStorage instance.
     *
     * @param feedId
     *            a hint for implementors
     * @return a Storage for the specified feed id
     */
    protected Storage getStorage() {
        if (sharedStorage == null) {
            try {
                Storage clientStorage = new FileStorage(Common.getClientRoot());
                Storage cacheStorage = new FileStorage(Common.getServerRoot());
                sharedStorage = new LuceneStorage(cacheStorage, clientStorage);
                sharedStorage = new CachingStorage(sharedStorage);
            } catch (IOException e) {
                log.error("Could not initialize storage", e);
            }
        }
        return sharedStorage;
    }

    /**
     * Override to return a custom adapter instance. This implementation
     * defaults to TrsstAdapter configured to use the result of
     * getStorageFromFeedId.
     *
     * @param feedId
     *            a hint for implementors
     * @return a TrsstAdapter for the specified feed id
     */
    protected TrsstAdapter getAdapterForFeedId(String feedId)
            throws IOException {
        if (Common.ROOT_ALIAS.equals(feedId)) {
            return new HomeAdapter(feedId, getStorage());
        }
        return new TrsstAdapter(feedId, getStorage());
    }

    private static Storage sharedStorage;

    public CollectionAdapter getCollectionAdapter(RequestContext request) {
        String feedId = request.getTarget().getParameter("collection");
        if (feedId != null && feedId.trim().length() == 0) {
            feedId = null;
        }
        if (feedId == null) {
            // default to subdomain name if any
            String host = request.getHeader("Host");
            if (host != null) {
                int i = host.lastIndexOf('.');
                if (i != -1) {
                    host = host.substring(0, i);
                    i = host.lastIndexOf('.');
                    if (i != -1) {
                        host = host.substring(0, i);
                        i = host.lastIndexOf('.');
                        if (i != -1) {
                            feedId = host.substring(0, i);
                        } else {
                            feedId = host;
                        }
                        try {
                            Integer.parseInt(feedId);
                            // it's a numeric address, not a name.
                            feedId = null;
                        } catch (NumberFormatException nfe) {
                            // it's a hostname; continue.
                        }
                    }
                }
            }
        }
        if (feedId == null) {
            feedId = Common.ROOT_ALIAS;
        }
        try {
            TrsstAdapter result = idsToAdapters.get(feedId);
            if (result == null) {
                result = getAdapterForFeedId(feedId);
                idsToAdapters.put(feedId, result);
            }
            return result;
        } catch (Throwable t) {
            log.error("Not found: id: " + feedId, t);
            return null;
        }
    }

    public class PaginationFilter implements Filter {
        public ResponseContext filter(RequestContext request, FilterChain chain) {
            RequestContextWrapper rcw = new RequestContextWrapper(request);
            rcw.setAttribute("offset", 10);
            rcw.setAttribute("count", 10);
            return chain.next(rcw);
        }
    }

    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(this
            .getClass());

    public String getTitle(RequestContext requsest) {
        // workspace info title
        return hostname;
    }

    /**
     * Returns some or all of the most active feeds hosted on this server. This
     * implementation calls Storage.getFeedIds(0,100). Override to think
     * different.
     */
    protected String[] getFeedIds(RequestContext request) {
        // arbitrary cap: get up to most active hundred.
        return getStorage().getFeedIds(0, 100);
    }

    public Collection<CollectionInfo> getCollections(RequestContext request) {
        LinkedList<CollectionInfo> result = new LinkedList<CollectionInfo>();
        Feed feed;
        Parser parser = Abdera.getInstance().getParser();
        CollectionInfo info;
        Storage storage = getStorage();
        for (String id : getFeedIds(request)) {
            try {
                feed = (Feed) parser.parse(
                        new StringReader(storage.readFeed(id))).getRoot();
                String title = feed.getTitle();
                // default title to id if null
                if (title == null) {
                    title = id;
                }
                info = new SimpleCollectionInfo(title, id, "text/plain",
                        "text/html", "text/xml", "image/png", "image/jpeg",
                        "image/gif", "image/svg+xml", "video/mp4");
                result.add(info);
            } catch (ParseException e) {
                log.warn("Could not parse collection info for feed: " + id
                        + " : " + e.toString());
            } catch (IOException e) {
                log.warn("Could not read collection info for feed: " + id
                        + " : " + e.toString());
            }
        }
        return result;
    }

    public Workspace asWorkspaceElement(RequestContext request) {
        Workspace workspace = request.getAbdera().getFactory().newWorkspace();
        workspace.setTitle(getTitle(request));
        for (CollectionInfo collection : getCollections(request))
            workspace.addCollection(collection.asCollectionElement(request));
        return workspace;
    }

    private static final class OrderedRegexTargetResolver extends
            RegexTargetResolver {

        // patterns is final in super
        Map<Pattern, TargetType> orderedPatterns = new LinkedHashMap<Pattern, TargetType>();

        // override to intercept pattern order
        public RegexTargetResolver setPattern(String pattern, TargetType type,
                String... fields) {
            // ugh: don't call super so we don't compile twice
            Pattern p = Pattern.compile(pattern);
            orderedPatterns.put(p, type);
            this.fields.put(p, fields);
            return this;
        }

        // override to exclude BaseTargetPath (and now to use ordered patterns)
        public Target resolve(Request request) {
            RequestContext context = (RequestContext) request;
            String uri = context.getTargetPath();
            if (uri.startsWith(context.getTargetBasePath())) {
                uri = uri.substring(context.getTargetBasePath().length());
            }
            // note: now first matching pattern wins
            for (Pattern pattern : orderedPatterns.keySet()) {
                Matcher matcher = pattern.matcher(uri);
                if (matcher.lookingAt()) {
                    TargetType type = this.orderedPatterns.get(pattern);
                    String[] fields = this.fields.get(pattern);
                    return getTarget(type, context, matcher, fields);
                }
            }
            return null;
        }
    };
}
TOP

Related Classes of com.trsst.server.AbderaProvider$OrderedRegexTargetResolver

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.