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 */
020package org.sonar.batch.index;
021
022import com.google.common.collect.Lists;
023import com.google.common.collect.Maps;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026import org.sonar.api.batch.events.DecoratorExecutionHandler;
027import org.sonar.api.batch.events.DecoratorsPhaseHandler;
028import org.sonar.api.batch.events.SensorExecutionHandler;
029import org.sonar.api.database.DatabaseSession;
030import org.sonar.api.database.model.MeasureData;
031import org.sonar.api.database.model.MeasureModel;
032import org.sonar.api.measures.Measure;
033import org.sonar.api.measures.PersistenceMode;
034
035import java.util.List;
036import java.util.Map;
037
038/**
039 * @since 2.7
040 */
041public 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}