package org.apache.solr.core; /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.apache.solr.common.SolrException; import org.apache.solr.common.util.XML; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.io.Writer; import java.nio.channels.FileChannel; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class SolrXMLSerializer { protected static Logger log = LoggerFactory .getLogger(SolrXMLSerializer.class); private final static String INDENT = " "; /** * @param w * Writer to use * @throws IOException If there is a low-level I/O error. */ void persist(Writer w, SolrXMLDef solrXMLDef) throws IOException { w.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); w.write("<solr"); Map<String,String> rootSolrAttribs = solrXMLDef.solrAttribs; Set<String> solrAttribKeys = rootSolrAttribs.keySet(); for (String key : solrAttribKeys) { String value = rootSolrAttribs.get(key); writeAttribute(w, key, value); } w.write(">\n"); Properties containerProperties = solrXMLDef.containerProperties; if (containerProperties != null && !containerProperties.isEmpty()) { writeProperties(w, containerProperties, " "); } // Output logging section if any if (solrXMLDef.loggingAttribs.size() > 0 || solrXMLDef.watcherAttribs.size() > 0) { w.write(INDENT + "<logging"); for (Map.Entry<String, String> ent : solrXMLDef.loggingAttribs.entrySet()) { writeAttribute(w, ent.getKey(), ent.getValue()); } w.write(">\n"); if (solrXMLDef.watcherAttribs.size() > 0) { w.write(INDENT + INDENT + "<watcher"); for (Map.Entry<String, String> ent : solrXMLDef.watcherAttribs.entrySet()) { writeAttribute(w, ent.getKey(), ent.getValue()); } w.write("/>\n"); } w.write(INDENT + "</logging>\n"); } w.write(INDENT + "<cores"); Map<String,String> coresAttribs = solrXMLDef.coresAttribs; Set<String> coreAttribKeys = coresAttribs.keySet(); for (String key : coreAttribKeys) { String value = coresAttribs.get(key); writeAttribute(w, key, value); } w.write(">\n"); for (SolrCoreXMLDef coreDef : solrXMLDef.coresDefs) { persist(w, coreDef); } // Shard handler section if (solrXMLDef.shardHandlerNode != null) { w.write(nodeToXML(solrXMLDef.shardHandlerNode)); } w.write(INDENT + "</cores>\n"); w.write("</solr>\n"); } private String nodeToXML(Node node) { try { TransformerFactory tfactory = TransformerFactory.newInstance(); Transformer tx = tfactory.newTransformer(); StringWriter buffer = new StringWriter(); tx.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); tx.transform(new DOMSource(node), new StreamResult(buffer)); return buffer.toString(); } catch (Exception e) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error transforming XML: " + e.getMessage()); } } /** Writes the cores configuration node for a given core. */ private void persist(Writer w, SolrCoreXMLDef coreDef) throws IOException { w.write(INDENT + INDENT + "<core"); Set<String> keys = coreDef.coreAttribs.keySet(); for (String key : keys) { writeAttribute(w, key, coreDef.coreAttribs.get(key)); } Properties properties = coreDef.coreProperties; if (properties == null || properties.isEmpty()) w.write("/>\n"); // core else { w.write(">\n"); writeProperties(w, properties, " "); w.write(INDENT + INDENT + "</core>\n"); } } private void writeProperties(Writer w, Properties props, String indent) throws IOException { for (Map.Entry<Object,Object> entry : props.entrySet()) { w.write(indent + "<property"); writeAttribute(w, "name", entry.getKey()); writeAttribute(w, "value", entry.getValue()); w.write("/>\n"); } } private void writeAttribute(Writer w, String name, Object value) throws IOException { if (value == null) return; w.write(" "); w.write(name); w.write("=\""); XML.escapeAttributeValue(value.toString(), w); w.write("\""); } void persistFile(File file, SolrXMLDef solrXMLDef) { log.info("Persisting cores config to " + file.getAbsolutePath()); File tmpFile = null; try { // write in temp first tmpFile = File.createTempFile("solr", ".xml", file.getParentFile()); java.io.FileOutputStream out = new java.io.FileOutputStream(tmpFile); Writer writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); try { persist(writer, solrXMLDef); } finally { writer.close(); out.close(); } // rename over origin or copy if this fails if (tmpFile != null) { if (tmpFile.renameTo(file)) tmpFile = null; else fileCopy(tmpFile, file); } } catch (java.io.FileNotFoundException xnf) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, xnf); } catch (java.io.IOException xio) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, xio); } finally { if (tmpFile != null) { if (!tmpFile.delete()) tmpFile.deleteOnExit(); } } } /** * Copies a src file to a dest file: used to circumvent the platform * discrepancies regarding renaming files. */ private static void fileCopy(File src, File dest) throws IOException { IOException xforward = null; FileInputStream fis = null; FileOutputStream fos = null; FileChannel fcin = null; FileChannel fcout = null; try { fis = new FileInputStream(src); fos = new FileOutputStream(dest); fcin = fis.getChannel(); fcout = fos.getChannel(); // do the file copy 32Mb at a time final int MB32 = 32 * 1024 * 1024; long size = fcin.size(); long position = 0; while (position < size) { position += fcin.transferTo(position, MB32, fcout); } } catch (IOException xio) { xforward = xio; } finally { if (fis != null) try { fis.close(); fis = null; } catch (IOException xio) {} if (fos != null) try { fos.close(); fos = null; } catch (IOException xio) {} if (fcin != null && fcin.isOpen()) try { fcin.close(); fcin = null; } catch (IOException xio) {} if (fcout != null && fcout.isOpen()) try { fcout.close(); fcout = null; } catch (IOException xio) {} } if (xforward != null) { throw xforward; } } static public class SolrXMLDef { Properties containerProperties; Map<String,String> solrAttribs; Map<String,String> coresAttribs; Map<String, String> loggingAttribs; Map<String, String> watcherAttribs; Node shardHandlerNode; List<SolrCoreXMLDef> coresDefs; } static public class SolrCoreXMLDef { Properties coreProperties; Map<String,String> coreAttribs; } }