Package gov.nasa.arc.mct.dbpersistence.service

Source Code of gov.nasa.arc.mct.dbpersistence.service.PersistenceServiceImpl

/*******************************************************************************
* Mission Control Technologies, Copyright (c) 2009-2012, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
* The MCT platform is 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.
*
* MCT includes source code licensed under additional open source licenses. See
* the MCT Open Source Licenses file included with this distribution or the About
* MCT Licenses dialog available at runtime from the MCT Help menu for additional
* information.
*******************************************************************************/
package gov.nasa.arc.mct.dbpersistence.service;

import gov.nasa.arc.mct.components.AbstractComponent;
import gov.nasa.arc.mct.components.ExtendedProperties;
import gov.nasa.arc.mct.components.ModelStatePersistence;
import gov.nasa.arc.mct.dbpersistence.access.InternalDBPersistenceAccess;
import gov.nasa.arc.mct.dbpersistence.dao.ComponentSpec;
import gov.nasa.arc.mct.dbpersistence.dao.DatabaseIdentification;
import gov.nasa.arc.mct.dbpersistence.dao.Disciplines;
import gov.nasa.arc.mct.dbpersistence.dao.MctUsers;
import gov.nasa.arc.mct.dbpersistence.dao.Tag;
import gov.nasa.arc.mct.dbpersistence.dao.TagAssociation;
import gov.nasa.arc.mct.dbpersistence.dao.TagAssociationPK;
import gov.nasa.arc.mct.dbpersistence.dao.ViewState;
import gov.nasa.arc.mct.dbpersistence.dao.ViewStatePK;
import gov.nasa.arc.mct.dbpersistence.search.QueryResult;
import gov.nasa.arc.mct.dbpersistence.service.StepBehindCache.Lookup;
import gov.nasa.arc.mct.platform.spi.PersistenceProvider;
import gov.nasa.arc.mct.platform.spi.Platform;
import gov.nasa.arc.mct.services.internal.component.ComponentInitializer;
import gov.nasa.arc.mct.services.internal.component.Updatable;
import gov.nasa.arc.mct.services.internal.component.User;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

import javax.persistence.Cache;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PersistenceServiceImpl implements PersistenceProvider {
  private static final Logger LOGGER = LoggerFactory.getLogger(PersistenceServiceImpl.class);
  private static final String PERSISTENCE_PROPS = "properties/persistence.properties";
  private static final String VERSION_PROPS = "properties/version.properties";
  private static final JAXBContext propContext;
  private static final ComponentIdComparator COMPONENT_ID_COMPARATOR = new ComponentIdComparator();
  private static final long MINIMUM_POLLING_INTERVAL = 10; // Don't poll more often than 10 ms
  private static final int DEFAULT_MAX_RESULTS = 100; // Default max search results
 
  private final ConcurrentHashMap<String, List<WeakReference<AbstractComponent>>> cache =
      new ConcurrentHashMap<String, List<WeakReference<AbstractComponent>>>();
  private Platform platform = null;

  private StepBehindCache<Set<String>> allUsers =
      new StepBehindCache<Set<String>>(new Lookup<Set<String>>() {
        public Set<String> lookup() {
          return lookupAllUsers();
        }
      });
  private StepBehindCache<List<String>> bootstrapComponentIds =
      new StepBehindCache<List<String>>(new Lookup<List<String>>() {
        public List<String> lookup() {
          return lookupBootstrapComponents();
        }
      })
 
  static {
    try {
      propContext = JAXBContext.newInstance(ExtendedProperties.class);
    } catch (JAXBException e) {
      throw new RuntimeException(e);
    }
  }
 
  static class ComponentIdComparator implements Comparator<AbstractComponent>, Serializable{
    private static final long serialVersionUID = 1834331905999908621L;

    public int compare(AbstractComponent o1, AbstractComponent o2) {
      return o1.getComponentId().compareTo(o2.getComponentId());
    }
  }
 
  private static final ThreadLocal<Set<AbstractComponent>> components = new ThreadLocal<Set<AbstractComponent>>(){
    @Override
    protected Set<AbstractComponent> initialValue() {
      return Collections.emptySet();
    }
  };
 
  private EntityManagerFactory entityManagerFactory;
   
  private PollTime lastPollTime;
  private Date lastModified;
  private long pollingInterval;
  private int maxResults = DEFAULT_MAX_RESULTS;
 
  public void bind(Platform platform) {
    this.platform = platform;
  }
 
  public void unbind(Platform platform) {
    if (this.platform == platform) {
      this.platform = null;
    }
  }
 
  public void setEntityManagerProperties(Properties p) {
    ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
    try {
      entityManagerFactory = Persistence.createEntityManagerFactory("MissionControlTechnologies", p);
    } finally {
      Thread.currentThread().setContextClassLoader(originalCL);
    }
  }

  public void activate(ComponentContext context) throws IOException {
    Properties persistenceProperties = getPersistenceProperties();
    setEntityManagerProperties(persistenceProperties);
    new InternalDBPersistenceAccess().setPersistenceService(this);
    checkDatabaseVersion();
   
    // Trigger initial lookup of users
    // (this leaves something in cache for subsequent lookups)
    allUsers.get();
   
    // Check for configuration of the polling interval (default is 3s)
    pollingInterval = 3000;
    try {
      String intervalString = persistenceProperties.getProperty("mct.database_pollInterval");
      if (intervalString != null) {
        pollingInterval = Long.parseLong(intervalString);
        if (pollingInterval < MINIMUM_POLLING_INTERVAL) {
          LOGGER.warn("Configured database polling interval {} too short. Defaulting to {} ms.",
              pollingInterval);
          pollingInterval = MINIMUM_POLLING_INTERVAL;
        }
      }
    } catch (NumberFormatException nfe) {
      // Stick with the default
    }
    try {
      String maxResultsString = persistenceProperties.getProperty("mct.database_maxResults");
      if (maxResultsString != null) {
        maxResults = Integer.parseInt(maxResultsString);
      }
    } catch (NumberFormatException nfe) {
      // Stick with the default
      maxResults = DEFAULT_MAX_RESULTS;
    }
   
        Timer databasePollingTimer = new Timer();
        databasePollingTimer.schedule(new TimerTask() {

            @Override
            public void run() {
              InternalDBPersistenceAccess.getService().updateComponentsFromDatabase();
            }
           
        }, pollingInterval, pollingInterval);

  }
 
  private Properties getPersistenceProperties() throws IOException {
    Properties properties = new Properties();
    InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(PERSISTENCE_PROPS);
    if (is == null)
      throw new IOException("Unable to get mct property file: " + PERSISTENCE_PROPS);
   
    try {
      properties.load(is);
    } finally {
      is.close();
    }
   
    // adjust user name and connection string properties for JPA
    final String dbUser = "mct.database_userName";
    final String dbPassword = "mct.database_password";
    final String dbConnectionURL = "mct.database_connectionUrl";
    final String dbName = "mct.database_name";
    final String dbProperties = "mct.database_properties";
    final String jdbcUrlProperty = "javax.persistence.jdbc.url";
    if (System.getProperty(jdbcUrlProperty) != null) {
      properties.put(jdbcUrlProperty, System.getProperty(jdbcUrlProperty));
    }
    final String userName = System.getProperty(dbUser, properties.getProperty(dbUser));
    final String pw = System.getProperty(dbPassword,properties.getProperty(dbPassword));
    if (userName != null) {
      properties.put("javax.persistence.jdbc.user", userName);
    }
    if (pw != null) {
      properties.put("javax.persistence.jdbc.password",pw);
    }
    String connectionURL = System.getProperty(dbConnectionURL, properties.getProperty(dbConnectionURL)) +
                 System.getProperty(dbName, properties.getProperty(dbName)) + "?" +
                 System.getProperty(dbProperties, properties.getProperty(dbProperties));
    if (!properties.containsKey(jdbcUrlProperty)) {
      properties.put(jdbcUrlProperty,connectionURL);
    }
   
    return properties;
  }
 
  private void checkDatabaseVersion() {
    // Don't check schema versions if a special JVM parameter is set.
        if (System.getProperty("mct.db.check-schema-version", Boolean.TRUE.toString()).equals(Boolean.TRUE.toString())) {
          Properties versionProperties = new Properties();
           try {
                 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(VERSION_PROPS);
             if (is == null)
               throw new IOException("Unable to get mct property file: " + VERSION_PROPS);
             try {
               versionProperties.load(is);
             } finally {
               is.close();
             }
             } catch (IOException e) {
                 throw new RuntimeException("Cannot load version properties (properties/version.properties)", e);
             }
            
           String schemaId;
             if ((schemaId = versionProperties.getProperty("mct.db.schema_id")) == null) {
                 throw new RuntimeException("required property mct.db.schema_id is undefined");
             }
            
             EntityManager em = entityManagerFactory.createEntityManager();
             try {
              
               TypedQuery<DatabaseIdentification> q = em.createNamedQuery("DatabaseIdentification.findSchemaId", DatabaseIdentification.class);
               DatabaseIdentification di = q.getSingleResult();
               if (!schemaId.equals(di.getValue())) {
                 String errMsg = "Mismatched schemaID.\nDeployed schema ID is " + di.getValue() +
                 "\nbut MCT requires schema ID " + schemaId;
                 LOGGER.error(errMsg);
                 throw new RuntimeException (errMsg);
               }
             } finally {
               em.close();
             }
       }
  }
 
  @Override
  public AbstractComponent getComponentFromStore(String componentId) {
    Cache c = entityManagerFactory.getCache();
    c.evict(ComponentSpec.class, componentId);
   
    return getComponent(componentId);
  }
 
  @Override
  public AbstractComponent getComponent(String componentId) {
    EntityManager em = entityManagerFactory.createEntityManager();
    ComponentSpec cs = null;
    try {
      cs = em.find(ComponentSpec.class, componentId);
    } finally {
      em.close();
    }
    if (cs != null) {
      return createAbstractComponent(cs);
    }
    return null;
  }

  @Override
  public Collection<AbstractComponent> getReferences(AbstractComponent component) {
    Collection<AbstractComponent> references = new ArrayList<AbstractComponent>();
    EntityManager em = entityManagerFactory.createEntityManager();
    try {
      TypedQuery<ComponentSpec> q = em.createNamedQuery("ComponentSpec.findReferencingComponents", ComponentSpec.class);
      q.setParameter("component", component.getComponentId());
      List<ComponentSpec> referencingComponents = q.getResultList();
      for (ComponentSpec cs:referencingComponents) {
        references.add(createAbstractComponent(cs));
      }
    } finally {
      em.close();
    }
   
    return references;
  }

  private boolean hasWorkUnitBeenStarted() {
    return getCurrentComponents() != Collections.<AbstractComponent>emptySet();
  }
 
  @Override
  public void startRelatedOperations() {
    assert entityManagerFactory != null;
    if (hasWorkUnitBeenStarted()) {
      throw new IllegalStateException("startRelatedOperations cannot be invoked until the last operations has been closed");
    }
    components.set(new TreeSet<AbstractComponent>(COMPONENT_ID_COMPARATOR));
  }

  @Override
  public void completeRelatedOperations(boolean save) {
    try {
      if (save) {
        persist(components.get());
      }
    } finally {
      components.remove();
    }
  }
 
  @Override
  public void addComponentToWorkUnit(AbstractComponent component) {
    if (hasWorkUnitBeenStarted()) {
      getCurrentComponents().add(component);
    }
  }
 
  public Collection<AbstractComponent> getCurrentComponents() {
    return components.get();
  }

  @Override
  public Set<String> getAllUsers() {
    return allUsers.get();
  }
 
  private Set<String> lookupAllUsers() {
    EntityManager em = entityManagerFactory.createEntityManager();
    Set<String> userNames = null;
    try {
      TypedQuery<MctUsers> q = em.createNamedQuery("MctUsers.findAll", MctUsers.class);
      List<MctUsers> users = q.getResultList();
      userNames = new HashSet<String>(users.size());
      for (MctUsers u:users) {
        userNames.add(u.getUserId());
      }
    } finally {
      em.close();
    }
   
    return userNames;
  }
 
  @Override
  public Collection<String> getUsersInGroup(String group) {
    EntityManager em = entityManagerFactory.createEntityManager();
    List<String> userNames = null;
    try {
      TypedQuery<MctUsers> q = em.createNamedQuery("MctUsers.findByGroup", MctUsers.class);
      q.setParameter("group", group);
      List<MctUsers> users = q.getResultList();
      userNames = new ArrayList<String>(users.size());
      for (MctUsers u:users) {
        userNames.add(u.getUserId());
      }
    } finally {
      em.close();
    }
   
    return userNames;
  }
 
  private ExtendedProperties createExtendedProperties(String props) {
      Unmarshaller unmarshaller;
      try {
        unmarshaller = propContext.createUnmarshaller();
        InputStream is = new ByteArrayInputStream(props.getBytes("ASCII"));
        return ExtendedProperties.class.cast(unmarshaller.unmarshal(is));
      } catch (JAXBException je) {
        throw new RuntimeException(je);
      } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e);
      }
  }
 
  private String generateStringFromView(ExtendedProperties viewState) {
    try {
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      Marshaller marshaller = propContext.createMarshaller();
      marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
      marshaller.setProperty(Marshaller.JAXB_ENCODING, "ASCII");
      marshaller.marshal(viewState, out);
      return out.toString("ASCII");
    } catch (JAXBException e) {
      throw new RuntimeException(e);
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
  }
 
  private ViewState createViewState(String viewType, String componentId, ExtendedProperties viewData, EntityManager em, ComponentSpec cs) {
    ViewStatePK viewStatePK = new ViewStatePK();
    viewStatePK.setComponentId(componentId);
    viewStatePK.setViewType(viewType);
    ViewState vs = em.find(ViewState.class, viewStatePK);
    if (vs == null) {
      vs = new ViewState();
      vs.setViewStatePK(viewStatePK);
      if (cs.getViewStateCollection() == null) {
        cs.setViewStateCollection(new ArrayList<ViewState>());
      }
      cs.getViewStateCollection().add(vs);
    }
    vs.setViewInfo(generateStringFromView(viewData));
    return vs;
  }
 
  private void updateComponentSpec(AbstractComponent ac,ComponentSpec cs,EntityManager em, boolean fullSave) {
    cs.setComponentId(ac.getComponentId());
    cs.setComponentName(ac.getDisplayName());
    cs.setOwner(ac.getOwner());
    cs.setCreatorUserId(ac.getCreator());
    cs.setComponentType(ac.getClass().getName());
    cs.setExternalKey(ac.getExternalKey());
    cs.setLastModified(lastModified);
   
    if (!fullSave) {
      return;
    }
    cs.setObjVersion(ac.getVersion());
    ModelStatePersistence persistence = ac.getCapability(ModelStatePersistence.class);
    if (persistence != null) {
      cs.setModelInfo(persistence.getModelState());
    }
   
    // save relationships
    // non optimal implementation as this will require a query to get the relationships
    cs.setReferencedComponents(new ArrayList<ComponentSpec>(ac.getComponents().size()));
    for (AbstractComponent c : ac.getComponents()) {
      ComponentSpec refCs = em.find(ComponentSpec.class, c.getComponentId());
      if (refCs == null) {
        // this can be null if the component has been deleted
        continue;
      }
      cs.getReferencedComponents().add(refCs);
    }
   
    // save views
    ComponentInitializer ci = ac.getCapability(ComponentInitializer.class);
    if (ci.getMutatedViewRoleProperties() != null) {
      for (Entry<String,ExtendedProperties> viewEntry : ci.getAllViewRoleProperties().entrySet()) {
        createViewState(viewEntry.getKey(), cs.getComponentId(), viewEntry.getValue(),em,cs);
      }
    }
  }
 
  @Override
  public void persist(Collection<AbstractComponent> componentsToPersist) {
    EntityManager em = entityManagerFactory.createEntityManager();
    lastModified = lastPollTime != null ?
        lastPollTime.getAdjustedNow() : // Predict database time
        getCurrentTimeFromDatabase();   // Or read it, if we haven't yet
    if (lastModified == null) {
      lastModified = new Date(); // Use system time as a fallback
    }
    try {
      em.getTransaction().begin();
      // first persist all new components, without relationships, model, and view states
      for (AbstractComponent nc : componentsToPersist) {
        if (nc.getCreationDate() == null) {
          ComponentSpec cs = new ComponentSpec();
          updateComponentSpec(nc, cs, em, false);
          em.persist(cs);
        }
      }
     
      // now persist the data
      for (AbstractComponent c : componentsToPersist) {
        updateComponentSpec(c, em.find(ComponentSpec.class, c.getComponentId()), em, true);
      }
      em.flush();
      em.getTransaction().commit();
      for (AbstractComponent c : componentsToPersist) {
        ComponentInitializer ci = c.getCapability(ComponentInitializer.class);
        ci.componentSaved();
        if (c.getCreationDate() == null) {
          ci.setCreationDate(em.find(ComponentSpec.class, c.getComponentId()).getDateCreated());
        }
        c.getCapability(Updatable.class).setVersion(em.find(ComponentSpec.class, c.getComponentId()).getObjVersion());
        c.componentSaved();
        List<WeakReference<AbstractComponent>> list = cache.get(c.getComponentId());
        if (list == null) {
          list = Collections.synchronizedList(new LinkedList<WeakReference<AbstractComponent>>());
          cache.put(c.getComponentId(), list);
        }
        list.add(new WeakReference<AbstractComponent>(c));
      }
    } catch(OptimisticLockException ole) {
      throw new gov.nasa.arc.mct.api.persistence.OptimisticLockException(ole);
    } finally {
      if (em.getTransaction().isActive()) {
        em.getTransaction().rollback();
      }
      em.close();
    }
  }

  @Override
  public void delete(Collection<AbstractComponent> componentsToDelete) {
    EntityManager em = entityManagerFactory.createEntityManager();
    try {
      em.getTransaction().begin();
      for (AbstractComponent component:componentsToDelete) {
        ComponentSpec componentToDelete = em.find(ComponentSpec.class, component.getComponentId());
        if (componentToDelete == null) {
          // component has already been deleted from database but not refreshed in the user interface
          continue;
        }
        TypedQuery<ComponentSpec> q = em.createNamedQuery("ComponentSpec.findReferencingComponents", ComponentSpec.class);
        q.setParameter("component", component.getComponentId());
        List<ComponentSpec> referencingComponents = q.getResultList();
        Date lastModified = lastPollTime != null ?
            lastPollTime.getAdjustedNow() : getCurrentTimeFromDatabase();
        if (lastModified == null) {
          lastModified = new Date();
        }
        for (ComponentSpec cs:referencingComponents) {
          cs.getReferencedComponents().remove(componentToDelete);
          cs.setLastModified(lastModified != null ? lastModified : new Date());
        }
        em.remove(componentToDelete);
      }
      em.getTransaction().commit();
    } finally {
      if (em.getTransaction().isActive()) {
        em.getTransaction().rollback();
      }
      em.close();
    }
   
  }
 
  @Override
  public boolean hasComponentsTaggedBy(String tagId) {
    EntityManager em = null;
    try {
      em = entityManagerFactory.createEntityManager();
      TypedQuery<TagAssociation> q = em.createNamedQuery("TagAssociation.getComponentsByTag", TagAssociation.class);
      q.setParameter("tagId", tagId);
      q.setMaxResults(1);
      return !q.getResultList().isEmpty();
    } finally {
      if (em != null) {
        em.close();
      }
    }
  }
 
  @Override
  public List<AbstractComponent> getReferencedComponents(
      AbstractComponent component) {
    List<AbstractComponent> references = new ArrayList<AbstractComponent>();
    EntityManager em = entityManagerFactory.createEntityManager();
    if (component.getComponentId() != null) {
      try {
        ComponentSpec reference = em.find(ComponentSpec.class, component.getComponentId());
          if (reference != null) {
          List<ComponentSpec> referencedComponents = reference.getReferencedComponents();
          for (ComponentSpec cs:referencedComponents) {
            if (cs != null) {
              AbstractComponent ac = createAbstractComponent(cs);
              references.add(ac);
            }
          }
        }
      } finally {
        em.close();
      }
    }
   
    return references;
  }
 
  @Override
  public User getUser(String userId) {
    EntityManager em = entityManagerFactory.createEntityManager();
    User u = null;
    try {
      MctUsers users = em.find(MctUsers.class, userId);
      if (users != null) {
        final String discipline = users.getDisciplineId().getDisciplineId();
        final String uId = users.getUserId();
       
        u = new User() {
          @Override
          public String getDisciplineId() {
            return discipline;
          }
         
          @Override
          public String getUserId() {
            return uId;
          }
         
          @Override
          public User getValidUser(String userID) {
            return getUser(userID);
          }
        };
      }
    } finally {
      em.close();
    }
    return u;
  }
 
    /**
     * Finds all the components by base display name regex pattern.
     * @param pattern - regex.
     * @param props <code>Properties</code> set arguments for SQL query.
     * @return QueryResult - search results.
     */
    @SuppressWarnings("unchecked")
    public QueryResult findComponentsByBaseDisplayedNamePattern(String pattern, Properties props) {
        String username = platform.getCurrentUser().getUserId();
       
        pattern = pattern.isEmpty() ? "%" : pattern.replace('*', '%');
       
        String countQuery = "select count(*) from component_spec c "
                        + "where c.creator_user_id like :creator "
                        + "and c.component_id not in (select component_id from component_spec where component_type = 'gov.nasa.arc.mct.core.components.TelemetryDataTaxonomyComponent' and component_name = 'All')"
                        + "and (c.component_type != 'gov.nasa.arc.mct.core.components.MineTaxonomyComponent' or c.owner = :owner) "
                        + "and c.component_name like :pattern ";
               
        String entitiesQuery = "select c.* from component_spec c "
            + "where c.creator_user_id like :creator "
            + "and c.component_id not in (select component_id from component_spec where component_type = 'gov.nasa.arc.mct.core.components.TelemetryDataTaxonomyComponent' and component_name = 'All')"
            + "and (c.component_type != 'gov.nasa.arc.mct.core.components.MineTaxonomyComponent' or c.owner = :owner) "
            + "and c.component_name like :pattern ";
       
        EntityManager em = entityManagerFactory.createEntityManager();
        try {
          Query q = em.createNativeQuery(countQuery);
                    
            q.setParameter("pattern", pattern);
            q.setParameter("owner", username);
            q.setParameter("creator", (props != null && props.get("creator") != null) ? props.get("creator") : "%" );   
            int count = ((Number) q.getSingleResult()).intValue();

            q = em.createNativeQuery(entitiesQuery, ComponentSpec.class);
            q.setMaxResults(maxResults);
            q.setParameter("pattern", pattern);
            q.setParameter("owner", username);
            q.setParameter("creator", (props != null && props.get("creator") != null) ? props.get("creator") : "%" );   
            List<ComponentSpec> daoObjects = q.getResultList();
            return new QueryResult(count, daoObjects);
        } catch (Exception t) {
            LOGGER.error("error executing query", t);
            return null;
        } finally {
          em.close();
        }       
    }
   
    AbstractComponent newAbstractComponent(ComponentSpec cs) {
      return platform.getComponentRegistry().newInstance(cs.getComponentType());
    }

    private AbstractComponent createAbstractComponent(ComponentSpec cs) {
      AbstractComponent ac = newAbstractComponent(cs);
    ComponentInitializer initializer = ac.getCapability(ComponentInitializer.class);
    initializer.setCreationDate(cs.getDateCreated());
    initializer.setCreator(cs.getCreatorUserId());
    initializer.setOwner(cs.getOwner());
    initializer.setId(cs.getComponentId());
    ac.setExternalKey(cs.getExternalKey());
    ac.setDisplayName(cs.getComponentName());
    ac.getCapability(Updatable.class).setVersion(cs.getObjVersion());
        ModelStatePersistence persister = ac.getCapability(ModelStatePersistence.class);
        if ((persister != null) && (cs.getModelInfo() != null)) {
            persister.setModelState(cs.getModelInfo());
        }
   
    // Add ac to cache
    List<WeakReference<AbstractComponent>> list = cache.get(cs.getComponentId());
    if (list == null) {
      list = Collections.synchronizedList(new LinkedList<WeakReference<AbstractComponent>>());
    }
    list.add(new WeakReference<AbstractComponent>(ac));
    cache.put(cs.getComponentId(), list);
    return ac;
    }
   
  @Override
  public <T extends AbstractComponent> T getComponent(String externalKey, Class<T> componentType) {
    EntityManager em = entityManagerFactory.createEntityManager();
    T comp = null;
    try {
      TypedQuery<ComponentSpec> q = em.createQuery("SELECT c FROM ComponentSpec c WHERE c.externalKey = :externalKey and c.componentType = :componentType", ComponentSpec.class);
      q.setParameter("externalKey", externalKey);
      q.setParameter("componentType", componentType.getName());
      List<ComponentSpec> cs = q.getResultList();
      if (!cs.isEmpty()) {
        AbstractComponent ac = createAbstractComponent(cs.get(0));
        comp = componentType.cast(ac);
      }
    } finally {
      em.close();
    }
    return comp;
  }
 
  private Date getCurrentTimeFromDatabase() {
    if (platform==null) {
      return null;
    }
    User currentUser = platform.getCurrentUser();
    if (currentUser == null)
      return null;
   
    String userId = currentUser.getUserId();
    EntityManager em = entityManagerFactory.createEntityManager();
    try {
      TypedQuery<Date> q = em.createQuery("SELECT CURRENT_TIMESTAMP FROM ComponentSpec c WHERE c.owner = :owner", Date.class);
      q.setParameter("owner", userId);
      return q.getSingleResult();
    } catch (NoResultException e) {
      // No result from database, so no current time
      // (database may not be fully initialized)
      return null;
    } finally {
      em.close();
    }
  }

  private void iterateOverChangedComponents(ChangedComponentVisitor v) {   
    if (lastPollTime == null) {
      Date storeTime = getCurrentTimeFromDatabase();
      if (storeTime == null)
        return;
      else
        lastPollTime = new PollTime(storeTime);
    }
   
        String query = "SELECT CURRENT_TIMESTAMP, c FROM ComponentSpec c WHERE c.lastModified BETWEEN ?1 AND CURRENT_TIMESTAMP";
        EntityManager em = entityManagerFactory.createEntityManager();       
        try {
            Query q = em.createQuery(query);
            q.setParameter(1, new Date(lastPollTime.getStoreTime().getTime() - pollingInterval), TemporalType.TIMESTAMP);
            final int MAX_CACHE_SIZE = 500;
            int iteration = 0;
            boolean done = false;
            while(!done) {
              q.setFirstResult(MAX_CACHE_SIZE * iteration);
              q.setMaxResults(MAX_CACHE_SIZE);
              @SuppressWarnings("rawtypes")
        List resultList = q.getResultList();
              for (int i=0; i<resultList.size(); i++) {
                for (Object obj : (Object[]) resultList.get(i)) {
                  if (obj instanceof ComponentSpec)
                    v.operateOnComponent((ComponentSpec) obj);
                  if (obj instanceof Date)
                    lastPollTime = new PollTime((Date) obj);
                }
              }     
              done = resultList.size() < MAX_CACHE_SIZE;
              iteration++;
              em.clear();
            }
        } catch (Exception t) {
            LOGGER.error("error executing query", t);
        } finally {
            em.close();
        }       

  }
 
  private void cleanCacheIfNecessary(String componentId, int latestVersion) {
    Cache c = entityManagerFactory.getCache();
    if (c.contains(ComponentSpec.class, componentId)) {
      EntityManager em = entityManagerFactory.createEntityManager();
      ComponentSpec cs = em.find(ComponentSpec.class, componentId);
      if (cs != null && cs.getObjVersion() < latestVersion) {
        c.evict(ComponentSpec.class, componentId);
      }
      em.close();
    }
  }
 
    private void updateComponentIfNecessary(final ComponentSpec c, final Collection<AbstractComponent> cachedComponents) {
      Collection<AbstractComponent> delegateComponets = new ArrayList<AbstractComponent>();
      for (final AbstractComponent ac : cachedComponents) {
        Updatable updatable = ac.getCapability(Updatable.class);
        updatable.setStaleByVersion(c.getComponentId(), c.getObjVersion());
        cleanCacheIfNecessary(c.getComponentId(), c.getObjVersion());
        if (ac.getWorkUnitDelegate() != null) {
          ac.getWorkUnitDelegate().getCapability(Updatable.class).setStaleByVersion(c.getComponentId(), c.getObjVersion());
          delegateComponets.add(ac.getWorkUnitDelegate());
        }
      }
      cachedComponents.addAll(delegateComponets);
      for (final AbstractComponent ac: cachedComponents) {
        if (ac.isStale()) {
              ac.resetComponentProperties(new AbstractComponent.ResetPropertiesTransaction() {
                 
                  @Override
                  public void perform() {
                      Updatable updatable = ac.getCapability(Updatable.class);
                      updatable.notifyStale();
                  }
              });
              LOGGER.debug("{} updated", c.getComponentName());
          }
      }
    }

    private int pollCounter = 0;
   
  @Override
  public void updateComponentsFromDatabase() {
    pollCounter++;
        iterateOverChangedComponents(
                new ChangedComponentVisitor() {
                    @Override
                    public void operateOnComponent(ComponentSpec c) {
                      // Evict referenced components from L2 cache
                      // TODO: Find alternate solution or refactor in order
                      //       to remove this explicit reference to Hibernate
                      ((HibernateEntityManagerFactory)entityManagerFactory)
                         .getSessionFactory()
                         .getCache().evictCollection(
                             ComponentSpec.class.getName() + ".referencedComponents",
                             c.getComponentId());
                      List<WeakReference<AbstractComponent>> list = cache.get(c.getComponentId());
                        if (list != null && !list.isEmpty()) {
                          Collection<AbstractComponent> cachedComponents = new ArrayList<AbstractComponent>(list.size());
                          synchronized(list) {
                            Iterator<WeakReference<AbstractComponent>> it = list.iterator();
                            while (it.hasNext()) {
                              AbstractComponent ac = it.next().get();
                              if (ac != null) {
                                cachedComponents.add(ac);
                              } else {
                                it.remove();
                              }
                            }
                          }
                          if (!cachedComponents.isEmpty())
                            updateComponentIfNecessary(c, cachedComponents);
                        }
                    }
                }
            );
        if (pollCounter % 1000 == 0)
          cleanCache();
  }
 
    public interface ChangedComponentVisitor {
        /**
         * This method is invoked for each component that has changed.
         * @param component that has changed
         */
        void operateOnComponent(ComponentSpec component);
    }

    @Override
  public List<AbstractComponent> getBootstrapComponents() {
      List<String> bootstrapCache = bootstrapComponentIds.get();

      if (bootstrapCache != null) {
        // Look up specific components based on bootstrap ids
        List<AbstractComponent> result = new ArrayList<AbstractComponent>(bootstrapCache.size());
        for (String id : bootstrapCache) {
          result.add(getComponent(id));
        }
        return result;
      }
   
    return null;       
  }
   
    private List<String> lookupBootstrapComponents() {
      List<ComponentSpec> cslist = null;
    EntityManager em = entityManagerFactory.createEntityManager();
    try {
      String userId = platform == null                  ? null :
                    platform.getCurrentUser() == null ? null :
                    platform.getCurrentUser().getUserId();
      TypedQuery<ComponentSpec> q = em.createQuery("SELECT t.componentSpec FROM TagAssociation t where t.tag.tagId = 'bootstrap:admin' or (t.tag.tagId = 'bootstrap:creator' and t.componentSpec.creatorUserId = :user)", ComponentSpec.class);
      q.setParameter("user", userId);
      cslist = q.getResultList();
    } finally {
      em.close();
    }
   
    if (cslist != null) {
      // Assemble list of component ids
      List<String> bootstrapCache = new ArrayList<String>();
      for (ComponentSpec cs : cslist) {
        bootstrapCache.add(cs.getComponentId());
      }
      return bootstrapCache;
    } else {
      return null;
    }
    }
   
    private void cleanCache() {
      Iterator<String> iterator = cache.keySet().iterator();
      while(iterator.hasNext()) {
        String componentId = iterator.next();
        List<WeakReference<AbstractComponent>> list = cache.get(componentId);
        boolean canBeDeleted = true;
        synchronized(list) {
          for (WeakReference<AbstractComponent> r : list) {
            if (r.get() != null) {
               canBeDeleted = false;
            }
          }
        }
        if (canBeDeleted)
          iterator.remove();
      }
    }

  @Override
  public void addNewUser(String userId, String groupId, AbstractComponent mysandbox, AbstractComponent dropbox) {
    String userDropboxesId = platform.getUserDropboxes().getComponentId();
     EntityManager em = entityManagerFactory.createEntityManager();
     try {
       em.getTransaction().begin();
       Disciplines group = em.find(Disciplines.class, groupId);
       if (group == null) {
         group = new Disciplines();
         group.setDisciplineId(groupId);
         em.persist(group);
       }
       MctUsers user = new MctUsers();
       user.setUserId(userId);
       user.setDisciplineId(group);
       em.persist(user);
 
       ComponentSpec mysandboxComponentSpec = new ComponentSpec();
       updateComponentSpec(mysandbox, mysandboxComponentSpec, em, true);
       em.persist(mysandboxComponentSpec);

       TagAssociationPK tagAssociationPK = new TagAssociationPK();
       tagAssociationPK.setComponentId(mysandbox.getComponentId());
       tagAssociationPK.setTagId("bootstrap:creator");
       TagAssociation tagAssociation = new TagAssociation();
       tagAssociation.setTagAssociationPK(tagAssociationPK);
       em.persist(tagAssociation);

       ComponentSpec dropboxComponentSpec = new ComponentSpec();
       updateComponentSpec(dropbox, dropboxComponentSpec, em, true);
       em.persist(dropboxComponentSpec);
   
       ComponentSpec userDropboxes = em.find(ComponentSpec.class, userDropboxesId);
       userDropboxes.getReferencedComponents().add(dropboxComponentSpec);       
       mysandboxComponentSpec.getReferencedComponents().add(dropboxComponentSpec);
   
       em.persist(userDropboxes);
       em.persist(mysandboxComponentSpec);
       em.getTransaction().commit();
     } finally {
       if(em.getTransaction().isActive())
         em.getTransaction().rollback();
       em.close();
     }
     // My Sandbox was marked a bootstrap, so we need to refresh cache
     // Otherwise, My Sandbox may not appear for the newly-created user.
     bootstrapComponentIds.refresh();
  }

  @Override
  public Map<String, ExtendedProperties> getAllProperties(String componentId) {
    EntityManager em = entityManagerFactory.createEntityManager();
    Map<String, ExtendedProperties> properties = new HashMap<String, ExtendedProperties>();
    try {
      ComponentSpec cs = em.find(ComponentSpec.class, componentId);
      if (cs != null) {
        for (ViewState vs: cs.getViewStateCollection()) {
          properties.put(vs.getViewStatePK().getViewType(), createExtendedProperties(vs.getViewInfo()));
        }
      }
      return properties;
    } finally {
      em.close();
    }
  }
 
  @Override
  public AbstractComponent getComponent(String externalKey,
      String componentType) {
    EntityManager em = entityManagerFactory.createEntityManager();
    AbstractComponent ac = null;
    try {
      TypedQuery<ComponentSpec> q = em
          .createQuery(
              "SELECT c FROM ComponentSpec c "
                  + "WHERE c.externalKey = :externalKey and c.componentType = :componentType",
              ComponentSpec.class);
      q.setParameter("externalKey", externalKey);
      q.setParameter("componentType", componentType);
      List<ComponentSpec> cs = q.getResultList();
      if (!cs.isEmpty()) {
          ac = createAbstractComponent(cs.get(0));
      }

    } finally {
      em.close();
    }
    return ac;
  }
 
  @Override
  public void tagComponents(String tag,
      Collection<AbstractComponent> components) {
    EntityManager em = entityManagerFactory.createEntityManager();
    try {
      em.getTransaction().begin();
      Tag t = em.find(Tag.class, tag);
      if (t == null) {
        t = new Tag();
        t.setTagId(tag);
        em.persist(t);
      }

      for (AbstractComponent component:components) {
        ComponentSpec cs = em.find(ComponentSpec.class, component.getComponentId());
        TagAssociation association = new TagAssociation();
        TagAssociationPK tagPK = new TagAssociationPK();
        tagPK.setComponentId(cs.getComponentId());
        tagPK.setTagId(t.getTagId());
        association.setTagAssociationPK(tagPK);
        cs.getTagAssociationCollection().add(association);
      }
     
      em.getTransaction().commit();
    } finally {
      if(em.getTransaction().isActive())
         em.getTransaction().rollback();
      em.close();
    }
    // Refresh the bootstrap cache if it appears to have changed.
    if (tag.startsWith("bootstrap:")) {
      bootstrapComponentIds.refresh();
    }
  }
 
  private static class PollTime {
    private Date storeTime;
    private long localTime;
   
    public PollTime(Date storeTime) {
      this.storeTime = storeTime;
      localTime = System.currentTimeMillis();
    }
   
    public Date getStoreTime() {
      return storeTime;
    }
   
    public Date getAdjustedNow() {
      long diff = System.currentTimeMillis() - localTime;
      return new Date(storeTime.getTime() + diff);
    }
  }
}
TOP

Related Classes of gov.nasa.arc.mct.dbpersistence.service.PersistenceServiceImpl

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.