001/* 002 * (C) Copyright 2014-2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo 018 */ 019 020package org.nuxeo.elasticsearch.config; 021 022import static org.nuxeo.elasticsearch.ElasticSearchConstants.ALL_FIELDS; 023import static org.nuxeo.elasticsearch.ElasticSearchConstants.BINARYTEXT_FIELD; 024import static org.nuxeo.elasticsearch.ElasticSearchConstants.DOC_TYPE; 025 026import java.io.File; 027import java.io.FileInputStream; 028import java.io.FileNotFoundException; 029import java.io.IOException; 030import java.io.InputStream; 031 032import org.apache.commons.io.IOUtils; 033import org.apache.commons.lang3.StringUtils; 034import org.nuxeo.common.Environment; 035import org.nuxeo.common.xmap.annotation.XNode; 036import org.nuxeo.common.xmap.annotation.XNodeList; 037import org.nuxeo.common.xmap.annotation.XObject; 038import org.nuxeo.elasticsearch.core.IncrementalIndexNameGenerator; 039 040/** 041 * XMap descriptor for configuring an index 042 * 043 * @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a> 044 */ 045@XObject(value = "elasticSearchIndex") 046public class ElasticSearchIndexConfig { 047 public static final String DEFAULT_SETTING_FILE = "default-doc-settings.json"; 048 049 public static final String DEFAULT_MAPPING_FILE = "default-doc-mapping.json"; 050 051 protected static final String DEFAULT_REPOSITORY_NAME = "default"; 052 053 protected static final String WRITE_SUFFIX = "-write"; 054 055 @XNode("@enabled") 056 protected boolean isEnabled = true; 057 058 @XNode("@name") 059 protected String name; 060 061 // @since 9.3 062 @XNode("@manageAlias") 063 protected boolean manageAlias; 064 065 // @since 9.3 066 @XNode("@writeAlias") 067 protected String writeAlias; 068 069 @XNode("@repository") 070 protected String repositoryName; 071 072 @XNode("@type") 073 protected String type = DOC_TYPE; 074 075 @XNode("@create") 076 protected boolean create = true; 077 078 @XNode("settings") 079 protected String settings; 080 081 @XNode("settings@file") 082 protected String settingsFile; 083 084 @XNode("mapping") 085 protected String mapping; 086 087 @XNode("mapping@file") 088 protected String mappingFile; 089 090 @XNodeList(value = "fetchFromSource/exclude", type = String[].class, componentType = String.class) 091 protected String[] excludes; 092 093 @XNodeList(value = "fetchFromSource/include", type = String[].class, componentType = String.class) 094 protected String[] includes; 095 096 @Override 097 public String toString() { 098 if (isEnabled()) { 099 return String.format("EsIndexConfig(%s, %s, %s)", getName(), getRepositoryName(), getType()); 100 } 101 return "EsIndexConfig disabled"; 102 } 103 104 public String[] getExcludes() { 105 if (excludes == null) { 106 return new String[] { BINARYTEXT_FIELD }; 107 } 108 return excludes; 109 } 110 111 public String[] getIncludes() { 112 if (includes == null || includes.length == 0) { 113 return new String[] { ALL_FIELDS }; 114 } 115 return includes; 116 } 117 118 public String getName() { 119 return name; 120 } 121 122 public String getType() { 123 return type; 124 } 125 126 public String getSettings() { 127 if (settingsFile != null) { 128 return contentOfFile(settingsFile); 129 } else if (settings != null && !settings.isEmpty()) { 130 return settings; 131 } 132 return contentOfFile(DEFAULT_SETTING_FILE); 133 } 134 135 protected String contentOfFile(String filename) { 136 try (InputStream stream = getResourceStream(filename)) { 137 return IOUtils.toString(stream, "UTF-8"); 138 } catch (IOException e) { 139 throw new IllegalArgumentException("Cannot load resource file: " + filename, e); 140 } 141 } 142 143 @SuppressWarnings("resource") // closed by caller 144 protected InputStream getResourceStream(String filename) { 145 // First check if the resource is available on the config directory 146 File file = new File(Environment.getDefault().getConfig(), filename); 147 if (file.exists()) { 148 try { 149 return new FileInputStream(file); 150 } catch (FileNotFoundException e) { 151 // try another way 152 } 153 } 154 155 // getResourceAsStream is needed getResource will not work when called from another module 156 InputStream ret = this.getClass().getClassLoader().getResourceAsStream(filename); 157 if (ret == null) { 158 // Then try to get it from jar 159 ret = this.getClass().getClassLoader().getResourceAsStream(filename); 160 } 161 if (ret == null) { 162 throw new IllegalArgumentException( 163 String.format("Resource file cannot be found: %s or %s", file.getAbsolutePath(), filename)); 164 } 165 return ret; 166 } 167 168 public String getMapping() { 169 if (mappingFile != null) { 170 return contentOfFile(mappingFile); 171 } else if (mapping != null && !mapping.isEmpty()) { 172 return mapping; 173 } 174 return contentOfFile(DEFAULT_MAPPING_FILE); 175 } 176 177 public boolean mustCreate() { 178 return create; 179 } 180 181 public String getRepositoryName() { 182 if (isDocumentIndex() && repositoryName == null) { 183 repositoryName = DEFAULT_REPOSITORY_NAME; 184 } 185 return repositoryName; 186 } 187 188 public boolean isEnabled() { 189 return isEnabled; 190 } 191 192 public void setEnabled(boolean isEnabled) { 193 this.isEnabled = isEnabled; 194 } 195 196 /** 197 * Return true if the index/mapping is associated with a Nuxeo document repository 198 * 199 * @since 7.4 200 */ 201 public boolean isDocumentIndex() { 202 return DOC_TYPE.equals(getType()); 203 } 204 205 /** 206 * Use {@code other} mapping and settings if not defined. 207 */ 208 public void merge(final ElasticSearchIndexConfig other) { 209 if (other == null) { 210 return; 211 } 212 if (mapping == null && other.mapping != null) { 213 mapping = other.mapping; 214 } 215 if (settings == null && other.settings != null) { 216 settings = other.settings; 217 } 218 if (mappingFile == null && other.mappingFile != null) { 219 mappingFile = other.mappingFile; 220 } 221 if (settingsFile == null && other.settingsFile != null) { 222 settingsFile = other.settingsFile; 223 } 224 } 225 226 // @since 9.3 227 public boolean hasExplicitWriteIndex() { 228 return StringUtils.isNotBlank(writeAlias); 229 } 230 231 // @since 9.3 232 public String writeIndexOrAlias() { 233 // Custom alias managed outside of Nuxeo 234 if (hasExplicitWriteIndex()) { 235 return writeAlias; 236 } 237 // Nuxeo manages the write alias 238 if (manageAlias) { 239 return name + WRITE_SUFFIX; 240 } 241 // Simple index 242 return name; 243 } 244 245 // @since 9.3 246 public boolean manageAlias() { 247 return manageAlias; 248 } 249 250 // @since 9.3 251 public String newWriteIndexForAlias(String aliasName, String oldIndexName) { 252 // TODO make the alias resolver configurable 253 return new IncrementalIndexNameGenerator().getNextIndexName(aliasName, oldIndexName); 254 } 255}