All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaushell.shaarlijavaapi.ShaarliClient Maven / Gradle / Ivy

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.vaushell.shaarlijavaapi;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Shaarli client. Now we have a JAVA access to Sebsauvage Shaarli (see http://sebsauvage.net/wiki/doku.php?id=php:shaarli)
 *
 * @author Fabien Vauchelles (fabien AT vauchelles DOT com)
 */
public class ShaarliClient
        implements AutoCloseable
{
    // PUBLIC
    /**
     * Construct the DAO with a specific http client
     *
     * @param client Specific HTTP client
     * @param endpoint Shaarli endpoint (like http://fabien.vauchelles.com/~fabien/shaarli)
     */
    public ShaarliClient( HttpClient client ,
                          String endpoint )
    {
        if ( client == null || endpoint == null )
        {
            throw new NullPointerException();
        }

        this.cm = null;
        this.client = client;
        this.endpoint = cleanEnding( endpoint );
    }

    /**
     * Construct the DAO
     *
     * @param endpoint Shaarli endpoint (like http://fabien.vauchelles.com/~fabien/shaarli)
     */
    public ShaarliClient( String endpoint )
    {
        if ( endpoint == null )
        {
            throw new NullPointerException();
        }

        this.endpoint = cleanEnding( endpoint );

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setUserAgent( params ,
                                         "Mozilla/5.0 (Windows NT 5.1; rv:15.0) Gecko/20100101 Firefox/15.0.1" );

        SchemeRegistry sr = new SchemeRegistry();

        sr.register( new Scheme( "http" ,
                                 80 ,
                                 PlainSocketFactory.getSocketFactory() ) );

        this.cm = new PoolingClientConnectionManager( sr );
        this.cm.setMaxTotal( 1000 );

        DefaultHttpClient lClient = new DefaultHttpClient( cm ,
                                                           params );
        lClient.setCookieStore( new BasicCookieStore() );

        this.client = lClient;
    }

    /**
     * Return Shaarli endpoint
     *
     * @return endpoint
     */
    public String getEndpoint()
    {
        return endpoint;
    }

    /**
     * Login
     *
     * @param login Login (must not be empty)
     * @param password Password (must not be empty)
     * @return true if logged or false otherwise
     */
    public boolean login( String login ,
                          String password )
    {
        if ( login == null || password == null )
        {
            throw new NullPointerException();
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug( "[" + getClass().getSimpleName() + "] login() : login=" + login );
        }

        String token;
        try
        {
            token = getToken( endpoint.toString() + "/?do=login" );
        }
        catch( IOException ex )
        {
            logger.error( "Cannot retrieve login token" ,
                          ex );
            return false;
        }

        try
        {
            loginImpl( login ,
                       password ,
                       token );
        }
        catch( IOException ex )
        {
            logger.error( "Cannot retrieve login token" ,
                          ex );
            return false;
        }

        setLinksByPage( MAX_LINKS_BY_PAGE );

        return true;
    }

    /**
     * Create a link.
     *
     * @param url Link's URL
     * @param title Link's title
     * @param description Link's description
     * @param tagsLinks tags (set, no duplicate please)
     * @param restricted Is the link private ?
     * @return generated id
     */
    public String createOrUpdateLink( String url ,
                                      String title ,
                                      String description ,
                                      Set tags ,
                                      boolean restricted )
    {
        return createOrUpdateLink( null ,
                                   url ,
                                   title ,
                                   description ,
                                   tags ,
                                   restricted );
    }

    /**
     * Create or modify a link. To modify, don't forgot the ID !
     *
     * @param id Link's ID. You can enforce one or let it be null (not the permalink id. Don't be confuse!)
     * @param url Link's URL
     * @param title Link's title
     * @param description Link's description
     * @param tags Links tags (set, no duplicate please)
     * @param restricted Is the link private ?
     * @return id
     */
    public String createOrUpdateLink( String id ,
                                      String url ,
                                      String title ,
                                      String description ,
                                      Set tags ,
                                      boolean restricted )
    {
        if ( url == null || title == null )
        {
            throw new NullPointerException();
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] createOrUpdateLink() : id=" + id + " / url=" + url + " / title=" + title + " / description=" + description + " / restricted=" + restricted );
        }

        String token;
        try
        {
            token = getToken( endpoint.toString() + "/?post" );
        }
        catch( IOException ex )
        {
            logger.error( "Cannot retrieve post token" ,
                          ex );
            return null;
        }

        HttpEntity responseEntity = null;
        try
        {
            // Exec request
            HttpPost post = new HttpPost( endpoint + "/?post=" + URLEncoder.encode( url ,
                                                                                    "UTF-8" ) );

            List nvps = new ArrayList<>();

            String returnID;
            if ( id != null )
            {
                returnID = id;
            }
            else
            {
                returnID = new SimpleDateFormat( "yyyyMMdd_HHmmss" ).format( new Date() );

            }
            nvps.add( new BasicNameValuePair( "lf_linkdate" ,
                                              returnID ) );

            nvps.add( new BasicNameValuePair( "lf_url" ,
                                              url ) );

            nvps.add( new BasicNameValuePair( "lf_title" ,
                                              title ) );

            if ( description != null )
            {
                nvps.add( new BasicNameValuePair( "lf_description" ,
                                                  description ) );
            }

            if ( restricted )
            {
                nvps.add( new BasicNameValuePair( "lf_private" ,
                                                  "true" ) );
            }

            StringBuilder sbTags = new StringBuilder();
            if ( tags != null )
            {
                for ( String tag : tags )
                {
                    if ( sbTags.length() > 0 )
                    {
                        sbTags.append( " " );
                    }

                    sbTags.append( tag );
                }
            }

            if ( sbTags.length() > 0 )
            {
                nvps.add( new BasicNameValuePair( "lf_tags" ,
                                                  sbTags.toString() ) );
            }

            nvps.add( new BasicNameValuePair( "save_edit" ,
                                              "Save" ) );
            nvps.add( new BasicNameValuePair( "token" ,
                                              token ) );
            nvps.add( new BasicNameValuePair( "returnurl" ,
                                              endpoint ) );

            post.setEntity( new UrlEncodedFormEntity( nvps ) );

            HttpResponse response = client.execute( post );
            responseEntity = response.getEntity();

            StatusLine sl = response.getStatusLine();
            if ( sl.getStatusCode() != 302 )
            {
                try( InputStream is = responseEntity.getContent() )
                {
                    throw new IOException( IOUtils.toString( is ) );
                }
            }

            return returnID;
        }
        catch( IOException ex )
        {
            logger.error( "Cannot post" ,
                          ex );
            return null;
        }
        finally
        {
            if ( responseEntity != null )
            {
                try
                {
                    EntityUtils.consume( responseEntity );
                }
                catch( IOException ex )
                {
                    throw new RuntimeException( ex );
                }
            }
        }
    }

    /**
     * Delete a link
     *
     * @param id Link's id (not the permalink id. Don't be confuse!)
     * @return
     */
    public boolean delete( String id )
    {
        if ( id == null )
        {
            throw new NullPointerException();
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] delete() : id=" + id );
        }

        String token;
        try
        {
            token = getToken( endpoint.toString() + "/?post" );
        }
        catch( IOException ex )
        {
            logger.error( "Cannot retrieve post token" ,
                          ex );
            return false;
        }

        HttpEntity responseEntity = null;
        try
        {
            // Exec request
            HttpPost post = new HttpPost( endpoint + "/?post" );

            List nvps = new ArrayList<>();

            nvps.add( new BasicNameValuePair( "lf_linkdate" ,
                                              id ) );

            nvps.add( new BasicNameValuePair( "delete_link" ,
                                              "" ) );

            nvps.add( new BasicNameValuePair( "token" ,
                                              token ) );

            post.setEntity( new UrlEncodedFormEntity( nvps ) );

            HttpResponse response = client.execute( post );
            responseEntity = response.getEntity();

            StatusLine sl = response.getStatusLine();
            if ( sl.getStatusCode() != 302 )
            {
                try( InputStream is = responseEntity.getContent() )
                {
                    throw new IOException( IOUtils.toString( is ) );
                }
            }

            return true;
        }
        catch( IOException ex )
        {
            logger.error( "Cannot delete" ,
                          ex );
            return false;
        }
        finally
        {
            if ( responseEntity != null )
            {
                try
                {
                    EntityUtils.consume( responseEntity );
                }
                catch( IOException ex )
                {
                    throw new RuntimeException( ex );
                }
            }
        }
    }

    /**
     * Get all used tags
     *
     * @return key/value with tag name and tag count
     */
    public Map getTags()
    {
        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] getTags()" );
        }

        HttpEntity responseEntity = null;
        try
        {
            // Exec request
            String execURL = endpoint + "/?do=tagcloud";
            HttpGet get = new HttpGet( execURL );

            HttpResponse response = client.execute( get );
            responseEntity = response.getEntity();

            StatusLine sl = response.getStatusLine();
            if ( sl.getStatusCode() == 200 )
            {
                try( InputStream is = responseEntity.getContent() )
                {
                    Map tags = new TreeMap();

                    Document doc = Jsoup.parse( is ,
                                                "utf-8" ,
                                                execURL );

                    Elements elts = doc.select( "#cloudtag *" );
                    Iterator itElts = elts.iterator();
                    while ( itElts.hasNext() )
                    {
                        int count = Integer.parseInt( itElts.next().text() );
                        String name = itElts.next().text();

                        tags.put( name ,
                                  count );
                    }

                    return tags;
                }
            }
            else
            {
                throw new IOException( sl.getReasonPhrase() );
            }
        }
        catch( IOException ex )
        {
            logger.error( "Cannot retrieve tags" ,
                          ex );
            return null;
        }
        finally
        {
            if ( responseEntity != null )
            {
                try
                {
                    EntityUtils.consume( responseEntity );
                }
                catch( IOException ex )
                {
                    throw new RuntimeException( ex );
                }
            }
        }
    }

    /**
     * Iterator to search all links in shaarli
     *
     * @return
     */
    public Iterator searchAllIterator()
    {
        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] searchAllIterator()" );
        }

        return iterator( null );
    }

    /**
     * Get all page's links
     *
     * @param page Page number (>=1)
     * @return List of links
     */
    public List searchAll( int page )
    {
        if ( page < 1 )
        {
            throw new IllegalArgumentException( "page must be greater or equals to 1" );
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] searchAll() : page=" + page );
        }

        String execURL = endpoint + "/?page=" + ( page );

        return parseLinks( execURL );
    }

    /**
     * Iterator to search links, filter by a term
     *
     * @param term Term (must not be null)
     * @return an iterator
     */
    public Iterator searchTermIterator( String term )
    {
        if ( term == null )
        {
            throw new NullPointerException();
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] searchTermIterator() : term=" + term );
        }

        try
        {
            return iterator( "searchterm=" + URLEncoder.encode( term ,
                                                                "UTF-8" ) );
        }
        catch( UnsupportedEncodingException ex )
        {
            throw new RuntimeException( ex );
        }
    }

    /**
     * Get all page's links, filter by a term
     *
     * @param page Page number (>=1)
     * @param term Tags array
     * @return List of links
     */
    public List searchTerm( int page ,
                                         String term )
    {
        if ( term == null )
        {
            throw new NullPointerException();
        }

        if ( page < 1 )
        {
            throw new IllegalArgumentException( "page must be greater or equals to 1" );
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] searchTerm() : page=" + page + " / term=" + term );
        }

        try
        {
            String execURL = endpoint + "/?page=" + ( page ) + "&searchterm=" + URLEncoder.encode( term ,
                                                                                                   "UTF-8" );

            return parseLinks( execURL );
        }
        catch( UnsupportedEncodingException ex )
        {
            throw new RuntimeException( ex );
        }
    }

    /**
     * Iterator to search links, filter by tags
     *
     * @param tags Tags array
     * @return an iterator
     */
    public Iterator searchTagsIterator( String... tags )
    {
        if ( tags == null || tags.length <= 0 )
        {
            throw new NullPointerException();
        }

        StringBuilder sb = new StringBuilder();
        for ( String tag : tags )
        {
            if ( sb.length() > 0 )
            {
                sb.append( " " );
            }

            sb.append( tag );
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] searchTagsIterator() : tags=" + sb.toString() );
        }

        try
        {
            return iterator( "searchtags=" + URLEncoder.encode( sb.toString() ,
                                                                "UTF-8" ) );
        }
        catch( UnsupportedEncodingException ex )
        {
            throw new RuntimeException( ex );
        }
    }

    /**
     * Get all page's links, filter by tags
     *
     * @param page Page number (>=1)
     * @param tags Tags array
     * @return List of links
     */
    public List searchTags( int page ,
                                         String... tags )
    {
        if ( page < 1 )
        {
            throw new IllegalArgumentException( "page must be greater or equals to 1" );
        }

        StringBuilder sb = new StringBuilder();
        for ( String tag : tags )
        {
            if ( sb.length() > 0 )
            {
                sb.append( " " );
            }

            sb.append( tag );
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] searchTags() : page=" + page + " / tags=" + sb.toString() );
        }

        try
        {
            String execURL = endpoint + "/?page=" + ( page ) + "searchtags=" + URLEncoder.encode( sb.toString() ,
                                                                                                  "UTF-8" );

            return parseLinks( execURL );
        }
        catch( UnsupportedEncodingException ex )
        {
            throw new RuntimeException( ex );
        }
    }

    /**
     * Set the number of links by page
     *
     * @param count Number of links
     */
    public void setLinksByPage( int count )
    {
        if ( count <= 0 )
        {
            throw new IllegalArgumentException();
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] setLinksByPage() : count=" + count );
        }

        HttpEntity responseEntity = null;
        try
        {
            // Exec request
            String execURL = endpoint + "/?linksperpage=" + count;
            HttpGet get = new HttpGet( execURL );

            HttpResponse response = client.execute( get );
            responseEntity = response.getEntity();

            StatusLine sl = response.getStatusLine();
            if ( sl.getStatusCode() != 200 )
            {
                throw new IOException( sl.getReasonPhrase() );
            }
        }
        catch( IOException ex )
        {
            logger.error( "Cannot set links per page" ,
                          ex );
        }
        finally
        {
            if ( responseEntity != null )
            {
                try
                {
                    EntityUtils.consume( responseEntity );
                }
                catch( IOException ex )
                {
                    throw new RuntimeException( ex );
                }
            }
        }
    }

    /**
     * Close the Shaarli connection
     */
    @Override
    public void close()
    {
        if ( cm != null )
        {
            cm.shutdown();
        }
    }
    // PRIVATE
    private final static int MAX_LINKS_BY_PAGE = 100;
    private final static Logger logger = LoggerFactory.getLogger( ShaarliClient.class );
    private HttpClient client;
    private PoolingClientConnectionManager cm;
    private String endpoint;

    private String getToken( String execURL )
            throws IOException
    {
        HttpEntity responseEntity = null;
        try
        {
            // Exec request
            HttpGet get = new HttpGet( execURL );

            HttpResponse response = client.execute( get );
            responseEntity = response.getEntity();

            StatusLine sl = response.getStatusLine();
            if ( sl.getStatusCode() != 200 )
            {
                throw new IOException( sl.getReasonPhrase() );
            }

            try( InputStream is = responseEntity.getContent() )
            {
                Document doc = Jsoup.parse( is ,
                                            "utf-8" ,
                                            execURL );

                Elements elts = doc.select( "input[name=token]" );
                if ( elts == null || elts.size() <= 0 )
                {
                    return null;
                }
                else
                {
                    return elts.get( 0 ).attr( "value" );
                }
            }
        }
        finally
        {
            if ( responseEntity != null )
            {
                EntityUtils.consume( responseEntity );
            }
        }
    }

    private void loginImpl( String login ,
                            String password ,
                            String token )
            throws IOException
    {
        HttpEntity responseEntity = null;
        try
        {
            // Exec request
            HttpPost post = new HttpPost( endpoint.toString() + "/?do=login" );

            List nvps = new ArrayList<>();
            nvps.add( new BasicNameValuePair( "login" ,
                                              login ) );
            nvps.add( new BasicNameValuePair( "password" ,
                                              password ) );
            nvps.add( new BasicNameValuePair( "token" ,
                                              token ) );
            nvps.add( new BasicNameValuePair( "returnurl" ,
                                              endpoint ) );
            post.setEntity( new UrlEncodedFormEntity( nvps ) );

            HttpResponse response = client.execute( post );
            responseEntity = response.getEntity();

            StatusLine sl = response.getStatusLine();
            if ( sl.getStatusCode() != 302 )
            {
                try( InputStream is = responseEntity.getContent() )
                {
                    throw new IOException( IOUtils.toString( is ) );
                }
            }
        }
        finally
        {
            if ( responseEntity != null )
            {
                EntityUtils.consume( responseEntity );
            }
        }
    }

    private List parseLinks( String execURL )
    {
        if ( execURL == null )
        {
            throw new NullPointerException();
        }

        if ( logger.isDebugEnabled() )
        {
            logger.debug(
                    "[" + getClass().getSimpleName() + "] parseLinks() : execURL=" + execURL );
        }

        List links = new ArrayList<>();

        HttpEntity responseEntity = null;
        try
        {
            // Exec request
            HttpGet get = new HttpGet( execURL );

            HttpResponse response = client.execute( get );
            responseEntity = response.getEntity();

            StatusLine sl = response.getStatusLine();
            if ( sl.getStatusCode() == 200 )
            {
                try( InputStream is = responseEntity.getContent() )
                {
                    Document doc = Jsoup.parse( is ,
                                                "utf-8" ,
                                                execURL );

                    Elements elts = doc.select( "ul li" );
                    if ( elts != null )
                    {
                        for ( Element elt : elts )
                        {
                            boolean restricted;
                            String cssClass = elt.attr( "class" );
                            if ( cssClass != null && cssClass.equals( "private" ) )
                            {
                                restricted = true;
                            }
                            else
                            {
                                restricted = false;
                            }

                            String ID = elt.select( "input[name=lf_linkdate" ).attr( "value" );
                            String permaID = elt.select( "a[name]" ).attr( "id" );
                            String title = elt.select( "span[class=linktitle]" ).text();
                            String description = elt.select( "div[class=linkdescription]" ).text();
                            String url = elt.select( "span[class=linkurl]" ).text();

                            ShaarliLink link = new ShaarliLink( ID ,
                                                                permaID ,
                                                                title ,
                                                                description ,
                                                                url ,
                                                                restricted );

                            Elements eltsTag = elt.select( "div[class=linktaglist] a" );
                            if ( eltsTag != null )
                            {
                                for ( Element eltTag : eltsTag )
                                {
                                    String tag = eltTag.text();

                                    link.addTag( tag );
                                }
                            }

                            links.add( link );
                        }
                    }

                    return links;
                }
            }
            else
            {
                throw new IOException( sl.getReasonPhrase() );
            }
        }
        catch( IOException ex )
        {
            logger.error( "Cannot links" ,
                          ex );
            return links;
        }
        finally
        {
            if ( responseEntity != null )
            {
                try
                {
                    EntityUtils.consume( responseEntity );
                }
                catch( IOException ex )
                {
                    throw new RuntimeException( ex );
                }
            }
        }
    }

    private Iterator iterator( final String query )
    {
        return new Iterator()
        {
            private List buffer = new ArrayList<>();
            private int bufferCursor = 0;
            private int page = 1;
            private String lastID = null;

            @Override
            public boolean hasNext()
            {
                if ( bufferCursor < buffer.size() )
                {
                    return true;
                }
                else
                {
                    buffer.clear();
                    bufferCursor = 0;

                    String execURL = endpoint + "/?page=" + ( page++ );
                    if ( query != null && query.length() > 0 )
                    {
                        execURL += "&" + query;
                    }

                    List links = parseLinks( execURL );
                    if ( links.isEmpty() )
                    {
                        return false;
                    }
                    else
                    {
                        String linksLastID = links.get( links.size() - 1 ).getID();
                        if ( lastID != null && lastID.equals( linksLastID ) )
                        {
                            return false;
                        }
                        else
                        {
                            lastID = linksLastID;

                            buffer.addAll( links );

                            return true;
                        }
                    }
                }
            }

            @Override
            public ShaarliLink next()
            {
                return buffer.get( bufferCursor++ );
            }

            @Override
            public void remove()
            {
                throw new UnsupportedOperationException();
            }
        };
    }

    private static String cleanEnding( String url )
    {
        if ( url.endsWith( "/" ) )
        {
            return url.substring( 0 ,
                                  url.length() - 1 );
        }
        else
        {
            return url;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy