package org.elasticsearch.index.fields; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.helpers.fields.SettingsHelper; import org.elasticsearch.index.mapper.*; public class ComputedFieldRootMapper implements RootMapper { private final static Map<Integer, ComputedFieldRootMapper> RootMappers = new HashMap<Integer, ComputedFieldRootMapper>(); private final String _name; private Map<String, Object> _mappings; private boolean _enabled; private List<ComputedFieldMapper> _children; private Integer _contentBuilderHash; private final ReentrantReadWriteLock _lock = new ReentrantReadWriteLock(); private final Lock _readLock = _lock.readLock(); private final Lock _writeLock = _lock.writeLock(); private final ESLogger _logger; protected ComputedFieldRootMapper(String name, Map<String, Object> mappings) { _logger = Loggers.getLogger("computed-fields", SettingsHelper.GetSettings(), name); _name = name; _mappings = mappings; _enabled = XContentMapValues.nodeBooleanValue(mappings.get("enabled"), false); } @Override public String name() { return _name; } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { try { if (_children == null) { synchronized(RootMappers) { if (_children == null) { _contentBuilderHash = System.identityHashCode(builder); RootMappers.put(_contentBuilderHash, this); _children = new ArrayList<ComputedFieldMapper>(); } } } builder.field(_name, _mappings); return builder; } catch (Throwable ex) { _logger.error("root mapper toXContent", ex); if (ex instanceof RuntimeException) throw (RuntimeException)ex; else throw new RuntimeException(ex); } } @Override public void parse(ParseContext context) throws IOException { } @Override public void merge(Mapper mergeWith, MergeContext mergeContext) throws MergeMappingException { try { ComputedFieldRootMapper m = (ComputedFieldRootMapper)mergeWith; if (!mergeContext.mergeFlags().simulate()) { _mappings = m._mappings; _enabled = m._enabled; if (_children != null) { _readLock.lock(); try { if (_children != null) { for (ComputedFieldMapper child : _children) { boolean deleted = true; if (m._children != null) { m._readLock.lock(); try { if (_children != null) { for (ComputedFieldMapper child2 : m._children) { if (child.name() == child2.name()) { deleted = false; break; } } } } finally { m._readLock.unlock(); } } child.deleted(deleted); } } } finally { _readLock.unlock(); } } reset(); } m.close(); } catch (Throwable ex) { _logger.error("root mapper merge", ex); if (ex instanceof RuntimeException) throw (RuntimeException)ex; else throw new RuntimeException(ex); } } @Override public void traverse(FieldMapperListener fieldMapperListener) { } @Override public void traverse(ObjectMapperListener objectMapperListener) { } @Override public void close() { reset(); } @Override public void preParse(ParseContext context) throws IOException { } @Override public void postParse(ParseContext context) throws IOException { try { if (!_enabled) return; if (_children == null) return; Map<String, Object> vars = new DocumentParametersWrapper(context.doc(), context.docMapper()); _readLock.lock(); try { if (_children != null) { for (ComputedFieldMapper child : _children) { child.execute(context, vars); } } } finally { _readLock.unlock(); } } catch (Throwable ex) { _logger.error("root mapper postParse", ex); if (ex instanceof RuntimeException) throw (RuntimeException)ex; else throw new RuntimeException(ex); } } @Override public boolean includeInObject() { return false; } public boolean enabled() { return _enabled && (_contentBuilderHash != null); } public void addChild(ComputedFieldMapper computedFieldMapper) { _writeLock.lock(); try { if (_children == null) _children = new ArrayList<ComputedFieldMapper>(); _children.add(computedFieldMapper); computedFieldMapper.root(this); } finally { _writeLock.unlock(); } } public void removeChild(ComputedFieldMapper computedFieldMapper) { if (_children == null) return; _writeLock.lock(); try { if (_children != null) { _children.remove(computedFieldMapper); computedFieldMapper.root(null); } } finally { _writeLock.unlock(); } if ((_children != null) && (_children.size() == 0)) reset(); } public void reset() { if (_contentBuilderHash != null) { synchronized(RootMappers) { if (_contentBuilderHash != null) { RootMappers.remove(_contentBuilderHash); _contentBuilderHash = null; _children = null; } } } } public static void Register(XContentBuilder builder, ComputedFieldMapper computedFieldMapper) { if (builder == null) return; Integer hash = System.identityHashCode(builder); ComputedFieldRootMapper root = RootMappers.get(hash); if (root == null) return; root.addChild(computedFieldMapper); } public static class TypeParser implements Mapper.TypeParser { public TypeParser() { } @Override public Mapper.Builder<?, ?> parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException { Builder builder = new Builder(name, node); return builder; } } public static class Builder extends Mapper.Builder<Builder, ComputedFieldRootMapper> { private Map<String, Object> _mappings; protected Builder(String name, Map<String, Object> mappings) { super(name); _mappings = mappings; } @Override public ComputedFieldRootMapper build(BuilderContext context) { return new ComputedFieldRootMapper(name, _mappings); } } }