Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/artifacts/CrossSectionArtifact.java @ 5779:ebec12def170
Datacage: Add a pool of builders to make it multi threadable.
XML DOM is not thread safe. Therefore the old implementation only allowed one thread
to use the builder at a time. As the complexity of the configuration
has increased over time this has become a bottleneck of the whole application
because it took quiet some time to build a result. Furthermore the builder code path
is visited very frequent. So many concurrent requests were piled up
resulting in long waits for the users.
To mitigate this problem a round robin pool of builders is used now.
Each of the pooled builders has an independent copy of the XML template
and can be run in parallel.
The number of builders is determined by the system property
'flys.datacage.pool.size'. It defaults to 4.
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Sun, 21 Apr 2013 12:48:09 +0200 |
parents | f2e7f07f608d |
children |
line wrap: on
line source
package de.intevation.flys.artifacts; import java.util.ArrayList; import java.util.List; import java.util.NavigableMap; import org.apache.log4j.Logger; import org.w3c.dom.Document; import de.intevation.artifacts.Artifact; import de.intevation.artifacts.ArtifactFactory; import de.intevation.artifacts.CallMeta; import de.intevation.flys.artifacts.access.RangeAccess; import de.intevation.flys.artifacts.model.CrossSectionFacet; import de.intevation.flys.artifacts.model.FastCrossSectionLineFactory; import de.intevation.flys.model.FastCrossSectionLine; import de.intevation.flys.model.CrossSection; import de.intevation.flys.model.CrossSectionLine; import de.intevation.flys.artifacts.model.CrossSectionFactory; import de.intevation.flys.artifacts.states.StaticState; import de.intevation.artifactdatabase.state.Facet; import de.intevation.artifactdatabase.state.FacetActivity; import de.intevation.artifactdatabase.state.State; import de.intevation.flys.artifacts.services.CrossSectionKMService; /** * Artifact describing a cross-section. */ public class CrossSectionArtifact extends StaticFLYSArtifact { /** Name of Artifact. */ public static final String CS_ARTIFACT_NAME = "cross_section"; /** Name of state. */ public static final String STATIC_STATE_NAME = "state.cross_section"; /** Name of data item keeping the position. */ public static final String DATA_KM = "cross_section.km"; /** Name of data item keeping the database id of this c.s.. */ public static final String DATA_DBID = "cross_section.dbid"; /** Name of data item flagging whether we think that we are master. */ public static final String DATA_IS_MASTER = "cross_section.master?"; /** Name of data item flagging whether we are the newest. */ public static final String DATA_IS_NEWEST = "cross_section.newest?"; /** Name of data item storing the previous possible km. */ public static final String DATA_PREV_KM = "cross_section.km.previous"; /** Name of data item storing the next possible km. */ public static final String DATA_NEXT_KM = "cross_section.km.next"; /** Own logger. */ private static final Logger logger = Logger.getLogger(CrossSectionArtifact.class); static { // TODO: Move to configuration. FacetActivity.Registry.getInstance().register( CS_ARTIFACT_NAME, new FacetActivity() { @Override public Boolean isInitialActive( Artifact artifact, Facet facet, String outputName ) { if (artifact instanceof FLYSArtifact) { FLYSArtifact flys = (FLYSArtifact)artifact; String data = flys.getDataAsString(DATA_IS_NEWEST); return data != null && data.equals("1"); } return null; } }); } /** Return given name. */ @Override public String getName() { return CS_ARTIFACT_NAME; } /** Store ids, create a CrossSectionFacet. */ @Override public void setup( String identifier, ArtifactFactory factory, Object context, CallMeta callMeta, Document data) { logger.info("CrossSectionArtifact.setup"); super.setup(identifier, factory, context, callMeta, data); String ids = getDatacageIDValue(data); if (ids != null && ids.length() > 0) { addStringData(DATA_DBID, ids); logger.debug("CrossSectionArtifacts db-id: " + ids); } else { throw new IllegalArgumentException("No attribute 'ids' found!"); } List<Facet> fs = new ArrayList<Facet>(); CrossSection cs = CrossSectionFactory.getCrossSection( Integer.parseInt(ids)); List<CrossSectionLine> csls = cs.getLines(); if (!csls.isEmpty()) { CrossSectionLine csl = csls.get(0); // Find min-km of cross sections, // then set DATA_KM to min(DATA_KM, minCross). double dataKm = Double.valueOf(getDataAsString(DATA_KM)); if (dataKm < csl.getKm().doubleValue()) { addStringData(DATA_KM, csl.getKm().toString()); } } fs.add(new CrossSectionFacet(0, cs.getDescription())); // Find out if we are newest and become master if so. boolean isNewest = CrossSectionFactory.isNewest(cs); String newString = (isNewest) ? "1" : "0"; addStringData(DATA_IS_NEWEST, newString); addStringData(DATA_IS_MASTER, newString); StaticState state = (StaticState) getCurrentState(context); if (!fs.isEmpty()) { addFacets(getCurrentStateId(), fs); } } /** Copy km where master-artifact "starts". */ @Override protected void initialize( Artifact artifact, Object context, CallMeta callMeta) { FLYSArtifact flys = (FLYSArtifact) artifact; RangeAccess rangeAccess = new RangeAccess(flys, null); double[] range = rangeAccess.getKmRange(); double min = 0.0f; if (range != null && range.length > 0) { min = range[0]; } this.addStringData(DATA_KM, Double.toString(min)); } /** Returns next possible km for a cross-section. */ public Double getNextKm() { return getDataAsDouble(DATA_NEXT_KM); } /** Returns previous possible km for a cross-section. */ public Double getPrevKm() { return getDataAsDouble(DATA_PREV_KM); } /** * Create and return a new StaticState with charting output. */ @Override public State getCurrentState(Object cc) { final List<Facet> fs = getFacets(getCurrentStateId()); StaticState state = new StaticState(STATIC_STATE_NAME) { @Override public Object staticCompute(List<Facet> facets) { if (facets != null) { facets.addAll(fs); } return null; } }; state.addDefaultChartOutput("cross_section", fs); return state; } /** * Get a list containing the one and only State. * @param context ignored. * @return list with one and only state. */ @Override protected List<State> getStates(Object context) { ArrayList<State> states = new ArrayList<State>(); states.add(getCurrentState(context)); return states; } // TODO all data access needs proper caching. /** * Get a DataItem casted to int (0 if fails). */ public int getDataAsIntNull(String dataName) { String val = getDataAsString(dataName); try { return Integer.parseInt(val); } catch (NumberFormatException e) { logger.warn("Could not get data " + dataName + " as int", e); return 0; } } /** Returns database-id of cross-section (from data). */ protected int getDBID() { return getDataAsIntNull(DATA_DBID); } /** * Return position (km) from data, 0 if not found. */ protected double getKm() { String val = getDataAsString(DATA_KM); try { return Double.valueOf(val); } catch (NumberFormatException e) { logger.warn("Could not get data " + DATA_KM + " as double", e); return 0; } } /** Returns true if artifact is set to be a "master" (other facets will * refer to this). */ public boolean isMaster() { return !getDataAsString(DATA_IS_MASTER).equals("0"); } /** * Get points of Profile of cross section at given kilometer. * * @return an array holding coordinates of points of profile ( * in the form {{x1, x2} {y1, y2}} ). */ public double [][] getCrossSectionData() { logger.info("getCrossSectionData() for cross_section.km " + getDataAsString(DATA_KM)); FastCrossSectionLine line = searchCrossSectionLine(); return line != null ? line.fetchCrossSectionProfile() : null; } /** * Get CrossSectionLine spatially closest to what is specified in the data * "cross_section.km", null if considered too far. * * It also adds DataItems to store the next and previous (numerically) * values at which cross-section data was recorded. * * @return CrossSectionLine closest to "cross_section.km", might be null * if considered too far. */ public FastCrossSectionLine searchCrossSectionLine() { double TOO_FAR = 1d; CrossSection crossSection = CrossSectionFactory .getCrossSection(getDBID()); if (logger.isDebugEnabled()) { logger.debug("dbid " + getDBID() + " : " + crossSection); } NavigableMap<Double, Integer> kms = CrossSectionKMService .getKms(crossSection.getId()); Double wishKM = getKm(); Double floor = kms.floorKey(wishKM); Double ceil = kms.ceilingKey(wishKM); Double nextKm; Double prevKm; double floorD = floor != null ? Math.abs(floor - wishKM) : Double.MAX_VALUE; double ceilD = ceil != null ? Math.abs(ceil - wishKM) : Double.MAX_VALUE; double km; if (floorD < ceilD) { km = floor; } else { km = ceil; } // If we are too far from the wished km, return null. if (Math.abs(km - wishKM) > TOO_FAR) { return null; } // Store next and previous km. nextKm = kms.higherKey(km); prevKm = kms.lowerKey(km); if (prevKm == null) { prevKm = -1d; } if (nextKm == null) { nextKm = -1d; } addStringData(DATA_PREV_KM, prevKm.toString()); addStringData(DATA_NEXT_KM, nextKm.toString()); return FastCrossSectionLineFactory .getCrossSectionLine(crossSection, km); } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :