001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2012 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    package org.sonar.batch.index;
021    
022    import com.google.common.collect.Lists;
023    import com.google.common.collect.Maps;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    import org.sonar.api.batch.events.DecoratorExecutionHandler;
027    import org.sonar.api.batch.events.DecoratorsPhaseHandler;
028    import org.sonar.api.batch.events.SensorExecutionHandler;
029    import org.sonar.api.database.DatabaseSession;
030    import org.sonar.api.database.model.MeasureData;
031    import org.sonar.api.database.model.MeasureModel;
032    import org.sonar.api.measures.Measure;
033    import org.sonar.api.measures.PersistenceMode;
034    
035    import java.util.List;
036    import java.util.Map;
037    
038    /**
039     * @since 2.7
040     */
041    public class MemoryOptimizer implements SensorExecutionHandler, DecoratorExecutionHandler, DecoratorsPhaseHandler {
042    
043      private static final Logger LOG = LoggerFactory.getLogger(MemoryOptimizer.class);
044    
045      private List<Measure> loadedMeasures = Lists.newArrayList();
046      private Map<Long, Integer> dataIdByMeasureId = Maps.newHashMap();
047      private DatabaseSession session;
048    
049      public MemoryOptimizer(DatabaseSession session) {
050        this.session = session;
051      }
052    
053      /**
054       * Remove data of a database measure from memory.
055       */
056      public void evictDataMeasure(Measure measure, MeasureModel model) {
057        if (PersistenceMode.DATABASE.equals(measure.getPersistenceMode())) {
058          MeasureData data = model.getMeasureData();
059          if (data != null && data.getId() != null) {
060            if (LOG.isDebugEnabled()) {
061              LOG.debug("Remove data measure from memory: " + measure.getMetricKey() + ", id=" + measure.getId());
062            }
063            measure.unsetData();
064            dataIdByMeasureId.put(measure.getId(), data.getId());
065          }
066        }
067      }
068    
069      public Measure reloadMeasure(Measure measure) {
070        if (measure.getId() != null && dataIdByMeasureId.containsKey(measure.getId()) && !measure.hasData()) {
071          Integer dataId = dataIdByMeasureId.get(measure.getId());
072          MeasureData data = session.getSingleResult(MeasureData.class, "id", dataId);
073          if (data == null) {
074            LoggerFactory.getLogger(getClass()).error("The MEASURE_DATA row with id " + dataId + " is lost");
075    
076          } else {
077            if (LOG.isDebugEnabled()) {
078              LOG.debug("Reload the data measure: " + measure.getMetricKey() + ", id=" + measure.getId());
079            }
080            measure.setData(data.getText());
081            loadedMeasures.add(measure);
082          }
083        }
084        return measure;
085      }
086    
087      public void flushMemory() {
088        if (LOG.isDebugEnabled() && !loadedMeasures.isEmpty()) {
089          LOG.debug("Flush " + loadedMeasures.size() + " data measures from memory: ");
090        }
091        for (Measure measure : loadedMeasures) {
092          measure.unsetData();
093        }
094        loadedMeasures.clear();
095      }
096    
097      boolean isTracked(Long measureId) {
098        return dataIdByMeasureId.get(measureId) != null;
099      }
100    
101      public void onSensorExecution(SensorExecutionEvent event) {
102        if (event.isEnd()) {
103          flushMemory();
104          session.commit();
105        }
106      }
107    
108      public void onDecoratorExecution(DecoratorExecutionEvent event) {
109        if (event.isEnd()) {
110          flushMemory();
111        }
112      }
113    
114      public void onDecoratorsPhase(DecoratorsPhaseEvent event) {
115        if (event.isEnd()) {
116          session.commit();
117        }
118      }
119    
120    }