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

com.quinsoft.zeidon.standardoe.TaskImpl 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.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;

import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.MapMaker;
import com.quinsoft.zeidon.Application;
import com.quinsoft.zeidon.CommitOptions;
import com.quinsoft.zeidon.DeserializeOi;
import com.quinsoft.zeidon.DropTaskCleanup;
import com.quinsoft.zeidon.EntityCache;
import com.quinsoft.zeidon.ObjectEngine;
import com.quinsoft.zeidon.OiSourceSelector;
import com.quinsoft.zeidon.SerializeOi;
import com.quinsoft.zeidon.Task;
import com.quinsoft.zeidon.TaskLogger;
import com.quinsoft.zeidon.View;
import com.quinsoft.zeidon.ZeidonException;
import com.quinsoft.zeidon.ZeidonLogger;
import com.quinsoft.zeidon.objectdefinition.EntityDef;
import com.quinsoft.zeidon.utils.StringInterpolator;

/**
 * Comparable interface is implemented so that we can easily sort tasks by ID.
 *
 * @author DG
 *
 */
class TaskImpl extends AbstractTaskQualification implements Task, Comparable
{
    private final TaskLogger           logger;
    private final ZeidonLogger         dblogger;
    private final ViewNameList         viewNameList;
    private final ConcurrentMap viewList;
    private final String               taskId;
    private final JavaObjectEngine     objectEngine;
    private boolean                    isValid;
    private final boolean              isSystemTask;

    private       String               tempDir;
    private       String               userId;
    private       String               description;

    /**
     * New versions of entities (created by the createTemperal... methods) are given
     * a unique version number so the OE can verify if all linked instances have been
     * versioned.
     **/
    private final AtomicLong versionCounter = new AtomicLong();
    private ScalaHelper scalaHelper;

    /**
     * Keep track of entity caches for this task.
     */
    private Map entityCacheMap;

    private Map>> configOverrideMap;

    private List cleanupWork;

    private final OiSourceSelector oiSourceSelector = new DefaultOiSourceSelector();

    TaskImpl(JavaObjectEngine objectEngine, Application app, String taskId)
    {
        super(app);
        isValid = true;
        this.taskId = taskId;
        this.objectEngine = objectEngine;

        // Check to see if this is the system task.  It's ok to use '==' instead of .equals()
        // because we control the creation of the system task.
        isSystemTask = ( taskId == ObjectEngine.ZEIDON_SYSTEM_APP_NAME );

        // viewList is a weak map.  This way we can get a list of views if necessary
        // (the browser needs this) but it won't hold onto views once they are no longer
        // in use.
        viewList = new MapMaker().concurrencyLevel( 2 ).weakKeys().makeMap();
        viewNameList = new ViewNameList();
        description = this.taskId;

        String prefix = String.format( " [%4s] ", taskId );
        logger = new TaskLogger( prefix );
        dblogger = new TaskLogger( prefix );
        String logLevel = this.readZeidonConfig( app.getName(), "InitialLogLevel" );
        if ( ! StringUtils.isBlank( logLevel ) )
        {
            logger.setLevel( logLevel );
            dblogger.setLevel( logLevel );
        }

        log().info( "Created new task for app %s, task ID = %s", app.getName(), taskId );
    }

    /**
     * This creates a boot-strap task used only for logging.
     */
    TaskImpl( JavaObjectEngine objectEngine )
    {
        super(null);
        taskId = "null";
        this.objectEngine = objectEngine;
        viewList = null;
        viewNameList = null;
        logger = new TaskLogger( " [bootstrap] " );
        dblogger = new TaskLogger( " [bootstrap] " );
        isSystemTask = false;
    }

    @Override
    public TaskImpl getTask()
    {
        return this;
    }

    @Override
    public TaskLogger log()
    {
        return logger;
    }

    @Override
    public ZeidonLogger dblog()
    {
        return dblogger;
    }

    /**
     * Adds the view to the internal weak viewList map.
     *
     * @param view
     */
    void addNewView( ViewImpl view )
    {
        viewList.put( view, Boolean.TRUE );
        if ( log().isTraceEnabled() )
            log().trace( "ViewList count: %d/%d", viewList.size(), viewList.keySet().size() );
    }

    void dropView( ViewImpl view )
    {
        // Try removing the view from all the name lists.  Note that this won't drop
        // the view name if it belongs to a different application list other than default.
        viewNameList.dropView( view );
        getSystemTask().viewNameList.dropView( view );
        getApplication().dropView( view );
        view.getApplication().dropView( view );

        // Don't remove the view from viewList.  It has a weak reference to the view so
        // it will eventually get cleaned up.  If the view doesn't disappear from the
        // viewList then the application must have a hold on the reference.  This will
        // make it easier to find resource leaks.
    }

    @Override
    public Collection getViewList()
    {
        // Return a copy of the view list.  This way the user application (most likely the
        //  browser) doesn't have to worry about views being deleted while iterating.
        ArrayList list = new ArrayList( viewList.keySet() );
        Collections.sort( list );
        return Collections.unmodifiableList( list );
    }

    @Override
    public int getViewCount()
    {
        return viewList.keySet().size();
    }

    @Override
    public String getTaskId()
    {
        return taskId;
    }

    @Override
    public String getUserId()
    {
        return userId;
    }

    @Override
    public void setUserId(String userId)
    {
        this.userId = userId;
    }

    /**
     * Calls dropTask().  This is implemented to support Closeable interface.
     */
    @Override
    public void close()
    {
        dropTask();
    }

    @Override
    public synchronized void dropTask()
    {
        // If this has already been dropped return silently.
        if ( ! isValid )
            return;

        log().debug( "Dropping task %s", taskId );

        if ( cleanupWork != null )
        {
            for ( DropTaskCleanup work : cleanupWork )
                work.taskDropped( this );
        }

        isValid = false;

        assert assertDropTask();

        // All we need to do is remove the task from the Object Engine's task list.
        // The GC will do everything else.
        objectEngine.dropTask( this );
    }

    /**
     * @return
     */
    private boolean assertDropTask()
    {
        // Make sure that no views named at the application level are associated with this task.
        for ( Application app : objectEngine.getApplicationList() )
        {
            for ( View view : app.getAllNamedViews() )
            {
                if ( ! view.getTask().isValid() )
                {
                    getSystemTask().log().error( "View %d is named at app level but is associated with an invalid task %d",
                                                  view.getId(), view.getTask().getTaskId() );
                    assert false : "View named at app level is associated with a invalid task";
                }

                // Is this a valid check?
                if ( view.getTask() != getSystemTask() )
                {
                    getSystemTask().log().error( "View %d is associated with an invalid task %d",
                                                  view.getId(), view.getTask().getTaskId() );
                    assert false : "There's a named view that is associated with a invalid task";
                }
            }
        }

        // If we get here then everythings ok.
        return true;
    }

    @Override
    public TaskImpl getSystemTask()
    {
        return objectEngine.getSystemTask();
    }

    @Override
    public int commitMultipleOis(CommitOptions options, View... views)
    {
        return commitMultipleOis( options, Arrays.asList( views ) );
    }

    /* (non-Javadoc)
     * @see com.quinsoft.zeidon.Task#commitMultipleOis(long, com.quinsoft.zeidon.dbhandler.DbConfiguration, com.quinsoft.zeidon.View[])
     */
    @Override
    public int commitMultipleOis( View... views)
    {
        return commitMultipleOis( new CommitOptions( this ), Arrays.asList( views ) );
    }

    @Override
    public int commitMultipleOis(Collection views)
    {
        return commitMultipleOis( new CommitOptions( this ), views );
    }

    /* (non-Javadoc)
     * @see com.quinsoft.zeidon.Task#commitMultipleOis(long, com.quinsoft.zeidon.dbhandler.DbConfiguration, com.quinsoft.zeidon.View[])
     */
    @Override
    public int commitMultipleOis( CommitOptions options, Collection views)
    {
        ArrayList list = new ArrayList();
        list.addAll( views );
        options.setViewList( list );
        CommitMultipleOIs committer = new CommitMultipleOIs( this, options, views );
        return committer.commit();
    }

    @Override
    public JavaObjectEngine getObjectEngine()
    {
        return objectEngine;
    }

    @Override
    public void dropNameForView(String name, View view)
    {
        viewNameList.dropNameForView( name, view );
    }

    @Override
    public View getViewByName(String name)
    {
        return viewNameList.getViewByName( name );
    }

    @Override
    public View getViewByKey( long key )
    {
        for ( View v : viewList.keySet() )
        {
            if ( v.getId() == key )
                return v;
        }

        return null;
    }

    @Override
    public void setNameForView(String name, View view)
    {
        viewNameList.setNameForView( name, view );
    }

    @Override
    public List getViewNameList(View view)
    {
        return viewNameList.getAllViewNames( view );
    }

    @Override
    public String toString()
    {
        return String.format( "Task %s app %s", getTaskId(), getApplication() );
    }

    @Override
    public boolean isValid()
    {
        return isValid;
    }

    @Override
    public String getTempDirectory()
    {
        if ( tempDir != null )
            return tempDir;

        tempDir = System.getProperty( "java.io.tmpdir" );
        if ( ! new File( tempDir ).exists() )
            throw new ZeidonException("Temp directory '%s' does not exist.  Create the directory or set ZEIDON_TEMP");

        tempDir = tempDir + File.separator;
        return tempDir;
    }

    @Override
    public int compareTo(TaskImpl o)
    {
        return taskId.compareTo( o.getTaskId() );
    }

    @Override
    public boolean equals( Object o )
    {
        if ( o instanceof TaskImpl )
            return compareTo( (TaskImpl) o ) == 0;

        return false;
    }

    long getNextVersionNumber()
    {
        //TODO: This should probably be set for the entire OE because we could have entities
        // from two different tasks with different versions.
        return versionCounter.incrementAndGet();
    }

    private void unlockAll( int length, Lock...locks)
    {
        assert length <= locks.length;

        for ( int i = 0; i < length; i++ )
        {
            try
            {
                locks[i].unlock();
            }
            catch ( Throwable t )
            {
                try
                {
                    log().error( "Error trying to unlock lock", t );
                }
                catch ( Throwable tt )
                {
                    // We need to make sure we unlock everything so if we catch an error while writing the
                    // error message we'll swallow it so we can keep going.
                }
            }
        }
    }

    /* (non-Javadoc)
     * @see com.quinsoft.zeidon.Task#setDescription(java.lang.String)
     */
    @Override
    public void setDescription( String description )
    {
        this.description = description;
    }

    /* (non-Javadoc)
     * @see com.quinsoft.zeidon.Task#getDescription()
     */
    @Override
    public String getDescription()
    {
        return description;
    }

    @Override
    public SerializeOi serializeOi()
    {
        return new SerializeOi();
    }

    @SuppressWarnings("unchecked")
    @Override
    public synchronized ScalaHelper getScalaHelper()
    {
        if ( scalaHelper == null )
        {
            String className = "com.quinsoft.zeidon.scala.ScalaHelperImpl";
            ClassLoader classLoader = getObjectEngine().getClassLoader( className );
            Class operationsClass;
            try
            {
                operationsClass = (Class) classLoader.loadClass( className );
                scalaHelper = operationsClass.newInstance();
                scalaHelper.setClassLoader( classLoader );
            }
            catch ( ClassNotFoundException e )
            {
                throw new ZeidonException("Couldn't load %s.  Do you have zeidon-scala in your classpath?", className );
            }
            catch ( Exception e )
            {
                throw ZeidonException.wrapException( e );
            }
        }

        return scalaHelper;
    }

    @Override
    public synchronized EntityCache getEntityCache( EntityDef entityDef )
    {
        if ( entityCacheMap != null )
        {
            EntityCache entityCache = entityCacheMap.get( entityDef.getErEntityToken() );
            if ( entityCache != null )
                return entityCache;
        }

        if ( isSystemTask )
            return null;

        return getSystemTask().getEntityCache( entityDef );
    }

    @Override
    public synchronized boolean setEntityCache( EntityCache entityCache )
    {
        if ( entityCacheMap == null )
            entityCacheMap = new HashMap<>();

        boolean b = entityCacheMap.containsKey( entityCache.getErEntityToken() );
        entityCacheMap.put( entityCache.getErEntityToken(), entityCache );
        return b;
    }

    @Override
    public synchronized String readZeidonConfig(Application application, String group, String key, String defaultValue)
    {
        String g = normalizeGroup( group );

        // Check to see if the value has been overridden for this task.
        if ( configOverrideMap != null )
        {
            Map> appMap = configOverrideMap.get( application );
            if ( appMap != null )
            {
                Map groupMap = appMap.get( group );
                if ( groupMap != null )
                {
                    if ( groupMap.containsKey( key ) )
                        return groupMap.get( key ); // If we get here then the value has been overridden.
                }
            }
        }

        return getObjectEngine().getZeidonPreferences( application ).get( g, key, defaultValue );
    }

    /**
     * Synchronized so it can be used for the system task.
     */
    @Override
    public synchronized void overrideZeidonConfig( Application application, String group, String key, String value )
    {
        group = normalizeGroup( group );

        if ( configOverrideMap == null )
            configOverrideMap = new HashMap<>();

        Map> appMap = configOverrideMap.get( application );
        if ( appMap == null )
        {
            appMap = new HashMap<>();
            configOverrideMap.put( application, appMap );
        }

        Map groupMap = appMap.get( group );
        if ( groupMap == null )
        {
            groupMap = new HashMap<>();
            appMap.put( group, groupMap );
        }

        groupMap.put( key, value );
    }

    @Override
    public void addTaskCleanupWork( DropTaskCleanup work )
    {
        if ( cleanupWork == null )
            cleanupWork = new ArrayList<>();

        cleanupWork.add( work );
    }

    @Override
    public DeserializeOi deserializeOi()
    {
        return new DeserializeOi( this ).setApplication( this.getApplication() );
    }

    @Override
    public String interpolate( String string )
    {
        return interpolate( string, null );
    }

    @Override
    public String interpolate( String string, Map variables )
    {
        StringInterpolator interpolator = new StringInterpolator();
        interpolator.setTask( this ).setVariables( variables );
        return interpolator.interpolate( string );
    }

    @Override
    public OiSourceSelector getOiSourceSelector()
    {
        return oiSourceSelector;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy