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

it.tidalwave.metadata.spi.MetadataItemProviderSupport Maven / Gradle / Ivy

The newest version!
/***********************************************************************************************************************
 *
 * blueMarine Metadata - open source media workflow
 * Copyright (C) 2007-2011 by Tidalwave s.a.s. (http://www.tidalwave.it)
 *
 ***********************************************************************************************************************
 *
 * 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.
 *
 ***********************************************************************************************************************
 *
 * WWW: http://bluemarine.tidalwave.it
 * SCM: https://kenai.com/hg/bluemarine~metadata-src
 *
 **********************************************************************************************************************/
package it.tidalwave.metadata.spi;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import it.tidalwave.util.logging.Logger;
import org.openide.loaders.DataObject;
import org.openide.util.Lookup;
import it.tidalwave.stopwatch.StopWatch;
import it.tidalwave.metadata.Metadata.FindOption;
import it.tidalwave.metadata.Metadata.NameFilter;
import it.tidalwave.metadata.Metadata.StorageType;
import it.tidalwave.metadata.Metadata.StoreOption;
import it.tidalwave.metadata.MetadataItemHolder;

/*******************************************************************************
 * 
 * This class provides a default implementation of a provider. Concrete subclasses
 * must just declare the class of the managed items in the constructor.
 * 
 * @author  Fabrizio Giudici
 * @version $Id$
 *
 ******************************************************************************/
public abstract class MetadataItemProviderSupport implements MetadataItemProvider
  {
    private static final String CLASS = MetadataItemProviderSupport.class.getName();
    private static final Logger logger = Logger.getLogger(CLASS);
    
    /***************************************************************************
     *
     * 
     **************************************************************************/
    protected static enum CreationStrategy
      {
        RAW
          {
            @Nonnull 
            public  Item createItem (@Nonnull final Class itemClass)
              throws Exception
              {
                return itemClass.newInstance();
              }
          },
        ENHANCED
          {
            @Nonnull 
            public  Item createItem (@Nonnull final Class itemClass)
              throws Exception
              {
                return MetadataItemEnhancer.createEnhancedItem(itemClass.newInstance());
              }
          };
        
        @Nonnull 
        public abstract  Item createItem (@Nonnull final Class itemClass)
          throws Exception;
      }
    
    @Nonnull 
    private final Class itemClass;
    
    @Nonnull 
    private final CreationStrategy creationStrategy;
    
    /***************************************************************************
     *
     * 
     **************************************************************************/
    private final static Comparator> holderSorterByLatestModificationTime = 
       new Comparator>()   
          {
            public int compare (@Nonnull final MetadataItemHolder holder1, 
                                @Nonnull final MetadataItemHolder holder2) 
              {
                final Date date1 = holder1.getLatestModificationTime();
                final Date date2 = holder2.getLatestModificationTime();
                
                if (date1 == null)
                  {
                    return (date2 == null) ? 0 : +1;
                  } 
                
                if (date2 == null)
                  {
                    return -1;  
                  }
                
                return -date1.compareTo(date2);
              }
          }; 

    /***************************************************************************
     *
     * Creates a new instance of this provider, specifying the class of the 
     * items it can manage.
     * 
     * @param  itemClass            the class of the managed items
     * @param  creationStrategy     the creation strategy for items
     * 
     **************************************************************************/
    public MetadataItemProviderSupport (@Nonnull final Class itemClass, 
                                        @Nonnull final CreationStrategy creationStrategy) 
      {
        this.itemClass = itemClass;
        this.creationStrategy = creationStrategy;
      }
    
    /***************************************************************************
     *
     * {@inheritDoc}
     * 
     **************************************************************************/
    @Nonnull 
    final public Class getItemClass()
      {
        return itemClass;
      }
    
    /***************************************************************************
     *
     * {@inheritDoc}
     * 
     **************************************************************************/
    @Nonnull 
    final public List> findOrCreateItems (@Nonnull final DataObject dataObject, 
                                                                   @Nonnull final FindOption ... options) 
      {
        logger.fine("findOrCreateItems(%s, %s)", dataObject, Arrays.toString(options));

        if (dataObject == null)
          {
            throw new IllegalArgumentException("dataObject is mandatory");
          }
        
        MetadataSpiUtils.validateOptions(options);
        final List> holders = findItems(itemClass, dataObject, options);
        
        // The new holder will have isAvailable() == false
        // FIXME: have a Map and make sure that the same instance for each DataObject is returned
        
        if (holders.isEmpty())
          {
            holders.add(createNewItem());
          }
        
        return holders;
      }
    
    /***************************************************************************
     *
     * {@inheritDoc}
     * 
     **************************************************************************/
    public int storeItem (@Nonnull final DataObject dataObject, 
                          @Nonnull final MetadataItemHolder holder, 
                          @Nonnull final StoreOption ... options)
      {
        logger.fine("storeItem(%s, %s, %s)", dataObject, holder, Arrays.toString(options));

        if (dataObject == null)
          {
            throw new IllegalArgumentException("dataObject is mandatory");
          }
        
        if (holder == null)
          {
            throw new IllegalArgumentException("holder is mandatory");
          }
        
        final NameFilter sinkNames = MetadataSpiUtils.findOption(options, NameFilter.ANY_NAME, NameFilter.class);
        final StorageType storageType = MetadataSpiUtils.findOption(options, StorageType.ANY_TYPE, StorageType.class);
        int result = 0;
        
        for (final MetadataItemSink sink : findMetadataItemSinks(itemClass))
          {
            if (sinkNames.matches(sink.getName()) && storageType.includes(sink.getType()))
              {
                try
                  {
                    sink.storeMetadataItem(dataObject, holder, options);
                    result++;
                  }
                catch (Throwable t)
                  {
                    // FIXME! This is important to pass RetryException from TxTask.
                    // It's important to have the retry logic to work for a LockException.
                    // But it's not good that we depend on that, here we should know nothing
                    // about JPA!
                    throw (t instanceof RuntimeException) ? (RuntimeException)t : new RuntimeException(t);
//                    logger.throwing(CLASS, "storeItem()", t);
//                    logger.warning("storeItem() threw exception: %s", sink));
                  }
              }
          }
        
        return result;
      }
    
    /***************************************************************************
     *
     * Subclasses must implement this method to return a new 
     * {@link MetadataHolder} for the new item. It is mandatory that the 
     * property {@link MetadataHolder#getLatestModificationTime()} is initially
     * null for the returned item.
     * 
     * @param  item  the new item
     * @return       a new holder for the item
     * 
     **************************************************************************/
    @Nonnull 
    protected abstract MetadataItemHolder createMetadataHolder (@Nonnull final Item item);
    
    /***************************************************************************
     *
     * Returns a sorted list of holders retrieved by some sources. The result
     * can be an empty list if nothing is found. The list is sorted by 
     * descending {@link MetadataHolder#getLatestModificationTime()}.
     * 
     * @param  itemClass    the type of metadata item to search
     * @param  dataObject   the bound object
     * @param  options      some options
     * @return              a list of holders
     * 
     **************************************************************************/
    @Nonnull 
    protected static  List> findItems (@Nonnull final Class itemClass,
                                                                      @Nonnull final DataObject dataObject, 
                                                                      @Nonnull final FindOption ... options) 
      {
        logger.fine("findItems(%s, %s, %s)", itemClass, dataObject, Arrays.toString(options));
        
        @Nonnull 
        final StorageType sourceType = MetadataSpiUtils.findOption(options, StorageType.ANY_TYPE, StorageType.class);
        final List> result = new ArrayList>();

        for (final MetadataItemSource source : findMetadataItemSources(itemClass)) 
          {
            if (sourceType.includes(source.getType()))
              {
                try
                  {
                    result.addAll(source.loadMetadataItems(dataObject));
                  }
                // Prevent a failing source from disrupting the whole operation
                catch (Throwable t)
                  {
                    logger.throwing(CLASS, "findItems()", t);
                    logger.warning("findItems() threw exception %s: %s", t, source);
                  }
              }
          }
          
        Collections.sort(result, holderSorterByLatestModificationTime);
        return result;
      }

    /***************************************************************************
     *
     * Returns a list of {@link MetadataItemSource}s suitable for the given
     * item type.
     * 
     * @param  itemClass  the metadata item type
     * @return            a list of suitable {@link MetadataItemSource}s
     * 
     **************************************************************************/
    @Nonnull 
    protected static  List> findMetadataItemSources (@Nonnull final Class itemClass) 
      {
        final List> result = new ArrayList>();

        for (final MetadataItemSource source : Lookup.getDefault().lookupAll(MetadataItemSource.class)) 
          {
            if (source.getItemClass().equals(itemClass)) 
              {
                result.add(source);
              }
          }

        logger.finest(">>>> sources for %s: %s", itemClass, result);
        return result;
      }
    
    /***************************************************************************
     *
     * Returns a list of {@link MetadataItemSink}s suitable for the given
     * item type.
     * 
     * @param  itemClass  the metadata item type
     * @return            a list of suitable {@link MetadataItemSink}s
     * 
     **************************************************************************/
    @Nonnull 
    private static  List> findMetadataItemSinks (@Nonnull final Class itemClass)
      {
        final List> result = new ArrayList>();

        for (final MetadataItemSink sink : Lookup.getDefault().lookupAll(MetadataItemSink.class))
          {
            if (sink.getItemClass().isAssignableFrom(itemClass))
              {
                result.add(sink);
              }
          }
        
        logger.finest(">>>> sinks for %s: %s", itemClass, result);
        return result;
      }

    /***************************************************************************
     *
     * 
     **************************************************************************/
    @Nonnull 
    private MetadataItemHolder createNewItem()
      {
        final StopWatch stopWatch = StopWatch.create(MetadataItemProviderSupport.class, "createNewItem/" + itemClass.getName());
        
        try 
          {
            final MetadataItemHolder holder = createMetadataHolder(creationStrategy.createItem(itemClass));
            
            if (holder.getLatestModificationTime() != null)
              {
                throw new AssertionError("MetadataHolders created by createMetadataHolder() must have getLatestModificationTime() == null");
              }
            
            return holder;
          } 
        catch (Exception e) 
          {
            throw new RuntimeException(e);
          } 
        finally
          {
            stopWatch.stop();
          }
      }
  }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy