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

com.quinsoft.zeidon.standardoe.CommitToRestServer Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/**
    This file is part of the Zeidon Java Object Engine (Zeidon JOE).

    Zeidon JOE 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.

    Zeidon JOE 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Zeidon JOE.  If not, see .

    Copyright 2009-2015 QuinSoft
 */
package com.quinsoft.zeidon.standardoe;

import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;

import com.quinsoft.zeidon.Application;
import com.quinsoft.zeidon.CommitOptions;
import com.quinsoft.zeidon.Committer;
import com.quinsoft.zeidon.DeserializeOi;
import com.quinsoft.zeidon.SerializeOi;
import com.quinsoft.zeidon.Task;
import com.quinsoft.zeidon.View;
import com.quinsoft.zeidon.ZeidonException;

/**
 * @author dgc
 *
 */
class CommitToRestServer implements Committer
{
    /**
     * The WriteOptions for creating the JSON stream.
     */
    private final static SerializeOi JSON_WRITE_OPTIONS = new SerializeOi().withIncremental();

    private List  viewList;
    private Task           task;
    private CommitOptions  options;
    private Application    application;
    private List originalList;

    /**
     * Set of the OIs in the original views.
     */
    private Set originalOiSet;

    /**
     * URL that specifies the server.  E.g. "http://localhost:8080"
     */
    private String serverUrl;

    /**
     * The fully qualified url, including the path.
     * E.g. "http://localhost:8080/activate/APP-NAME".
     */
    private String url;

    /**
     * Map that lets logic find and EI by tags.  The tag is the EntityKey of the original
     * EIs (before commit) and the EI is from the new OIs.
     */
    private Map tagMap;

    /**
     * True if there are EIs in the list of views that are linked to EI's that
     * belong to OIs outside of this commit.  This will have some impact on how
     * we merge the results from the remote server.
     */
    private boolean hasExternallyLinkedInstances = false;

    @Override
    public void init( Task task, List list, CommitOptions options )
    {
        this.task = task;
        this.viewList = new ArrayList();
        this.options = options;
        this.application = options.getApplication();

        originalList = list;
        originalOiSet = new HashSet();
        for ( View v : list )
        {
            if ( application == null )
                v.getApplication();

            ViewImpl newView = ((InternalView) v).getViewImpl();
            ObjectInstance oi = newView.getObjectInstance();
            if ( ! originalOiSet.contains( oi ) )
            {
                originalOiSet.add( oi );
                viewList.add( newView );
            }
        }

        if ( viewList.size() != 1 )
            throw new ZeidonException( "Committing via http currently only supports a single view at a time." );

        serverUrl = options.getOiSourceUrl();
        url = String.format( "%s/%s/%s", serverUrl, application.getName(), viewList.get( 0 ).getLodDef().getName() );
        getTask().log().debug( "Committing to REST URL: %s", url );
        assert serverUrl.startsWith( "http://" ) || serverUrl.startsWith( "https://" ) : "Unexpected oiSourceUrl " + serverUrl;
    }

    @Override
    public List commit()
    {
        try
        {
//            copyEntityKeysToTags();

            String json = task.serializeOi().asJson().withIncremental().addViews( viewList ).toStringWriter().toString();
            List views = makePostCall( json );

            if ( views.size() != 1 )
                throw new ZeidonException( "Something went wrong: we have the wrong number of views" );

            ViewImpl internalView = ((InternalView) views.get( 0 )).getViewImpl();
            viewList.get( 0 ).replaceObjectInstance( internalView.getObjectInstance() );

//            supersedeEis( views );
            return originalList;
        }
        catch ( Exception e )
        {
            throw ZeidonException.wrapException( e );
        }
    }

    /**
     * This takes the views returned from the server and updates the original EIs to point
     * to the new EIs.
     *
     * @param newViews Views returned from the server.
     */
    private void supersedeEis( List newViews )
    {
        // Create a map of the tags that points to the new EIs.
        createTagMap( newViews );

        // Loop through all the EIs in the original OIs and set them to be superseded
        // by the new EIs.
        for ( ObjectInstance origOi: originalOiSet )
        {
            ObjectInstance newOi = null;

            for ( EntityInstanceImpl origEi = origOi.getRootEntityInstance();
                  origEi != null;
                  origEi = origEi.getNextHier() )
            {
                String tag = origEi.getTag();
                assert ! StringUtils.isBlank( tag ) : "Tag is blank!";

                EntityInstanceImpl newEi = tagMap.get( tag );
                if ( newEi == null )
                {
                    // If we get here then the original EI should have been deleted.  We can't
                    // verify that because it's possible that it was deleted by the commit constraint
                    // which is only run on the server.  We'll set the prev/next pointers to isolate
                    // this EI.
                }
                else
                {
                    newOi = newEi.getObjectInstance();

                    // Update any linked instances to be linked with the new EI.
                    for ( EntityInstanceImpl linked: origEi.getLinkedInstances() )
                    {
                        // If the linked EI is part of the current commit then skip it
                        // because it will be handled as part of the main loop.
                        if ( originalOiSet.contains( linked.getObjectInstance() ) )
                            continue;

                        // Reset the linked instance to be linked with newEi.
                        linked.linkInstances( newEi );
                        linked.setCreated( false );
                        linked.setUpdated( false );
                    }

                    // Set the next version.  This will effectively update any cursors pointing at
                    // origEi.
                    origEi.setNextVersion( newEi );
                }

                // It's possible a cursor is pointing to this EI.  Set prev/next pointers to
                // null so everything else can be GC'd.
                origEi.setEiPointersToNull();

            } // for each EI...

            if ( newOi == null )
            {
                // If we get here then there are *no* EI's from the origOi that are in the new
                // OIs.  This can happen if all the EI's were deleted.  Reset origOi to indicate
                // that it's empty.
                origOi.setRootEntityInstance( null );
            }
            else
            {
                // TODO: copy OI settings, set root EI.
                origOi.setRootEntityInstance( newOi.getRootEntityInstance() );
            }

            origOi.setUpdated( false );
            origOi.setUpdatedFile( false );

        } // for each OI...
    }

    private void createTagMap(List views)
    {
        tagMap = new HashMap();
        for ( View v : views )
        {
            ObjectInstance oi = ((ViewImpl) v).getObjectInstance();
            for ( EntityInstanceImpl ei = oi.getRootEntityInstance();
                  ei != null;
                  ei = ei.getNextHier() )
            {
                tagMap.put( ei.getTag(), ei );
            }
        }
    }

    private List makePostCall( String json )
    {
        String stringResponse = null;
        CloseableHttpResponse response = null;

        try
        {
            HttpPost post = new HttpPost( url );
            StringEntity entity = new StringEntity( json );
            post.setEntity( entity );
            post.setHeader( "Content-Type", "application/json" );

            response = ZeidonHttpClient.getClient( task, application ).callPost( post );
            InputStream stream = response.getEntity().getContent();
            StatusLine status = response.getStatusLine();
            task.log().info( "Status from http activate = %s", status );
            int statusCode = status.getStatusCode();

            // If we're in debug mode, print out the results.
            if ( task.log().isDebugEnabled() || statusCode != 200 )
            {
                StringWriter writer = new StringWriter();
                IOUtils.copy(stream, writer, "UTF-8");
                stringResponse = writer.toString();
                task.log().debug( "REST response: %s", stringResponse );
                stream = IOUtils.toInputStream(stringResponse, "UTF-8");
            }

            if ( statusCode != 200 )
                throw new ZeidonException( "http activate failed with status %s", status );

            List views = new DeserializeOi( getTask() )
                                        .asJson()
                                        .fromInputStream( stream )
                                        .activate();

            return views;
        }
        catch ( Exception e )
        {
            throw ZeidonException.wrapException( e ).appendMessage( "web URL = %s", url );

        }
        finally
        {
            if ( response != null )
            {
                EntityUtils.consumeQuietly(response.getEntity());
                IOUtils.closeQuietly( response );
            }
        }

    }
    /**
     * Copy the EntityKeys to the tag.  We'll use these to merge the OIs returned from the server.
     * Also sets hasExternallyLinkedInstances.
     */
    private void copyEntityKeysToTags()
    {
        for ( ObjectInstance oi: originalOiSet )
        {
            for ( EntityInstanceImpl ei = oi.getRootEntityInstance();
                  ei != null;
                  ei = ei.getNextHier() )
            {
                ei.setTag( Long.toString( ei.getEntityKey() ) );

                // Check to see if any linked instances are outside of the OI set.
                if ( ! hasExternallyLinkedInstances ) // Have we already determined there are external EIs?
                {
                    // No.  Loop through linked instances to see if they belong to an OI that
                    // is not part of this commit.
                    for ( EntityInstanceImpl linked : ei.getLinkedInstances() )
                    {
                        if ( ! originalOiSet.contains( linked.getObjectInstance() ) )
                        {
                            hasExternallyLinkedInstances = true;
                            break; // We don't need to search any more.
                        }
                    }
                }
            }
        }
    }

    private Task getTask()
    {
        return task;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy