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 com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import org.apache.solr.common.SolrException;
import org.apache.solr.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Date;
import java.util.List;
import java.util.Properties;

/**
 * Persists CoreDescriptors as properties files
 */
public class CorePropertiesLocator implements CoresLocator {

  public static final String PROPERTIES_FILENAME = "core.properties";

  private static final Logger logger = LoggerFactory.getLogger(CoresLocator.class);

  private final File rootDirectory;

  public CorePropertiesLocator(String coreDiscoveryRoot) {
    this.rootDirectory = new File(coreDiscoveryRoot);
  }

  @Override
  public void create(CoreContainer cc, CoreDescriptor... coreDescriptors) {
    for (CoreDescriptor cd : coreDescriptors) {
      File propFile = new File(new File(cd.getInstanceDir()), PROPERTIES_FILENAME);
      if (propFile.exists())
        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
                                "Could not create a new core in " + cd.getInstanceDir()
                              + "as another core is already defined there");
      writePropertiesFile(cd, propFile);
    }
  }

  // TODO, this isn't atomic!  If we crash in the middle of a rename, we
  // could end up with two cores with identical names, in which case one of
  // them won't start up.  Are we happy with this?

  @Override
  public void persist(CoreContainer cc, CoreDescriptor... coreDescriptors) {
    for (CoreDescriptor cd : coreDescriptors) {
      File propFile = new File(new File(cd.getInstanceDir()), PROPERTIES_FILENAME);
      writePropertiesFile(cd, propFile);
    }
  }

  private void writePropertiesFile(CoreDescriptor cd, File propfile)  {
    Properties p = buildCoreProperties(cd);
    OutputStream os = null;
    try {
      os = new FileOutputStream(propfile);
      Writer writer = new OutputStreamWriter(os, Charsets.UTF_8);
      p.store(writer, "Written by CorePropertiesLocator on " + new Date());
      writer.close();
    }
    catch (IOException e) {
      logger.error("Couldn't persist core properties to {}: {}", propfile.getAbsolutePath(), e);
    }
    finally {
      if (os != null)
        IOUtils.closeQuietly(os);
    }
  }

  @Override
  public void delete(CoreContainer cc, CoreDescriptor... coreDescriptors) {
    for (CoreDescriptor cd : coreDescriptors) {
      File instanceDir = new File(cd.getInstanceDir());
      File propertiesFile = new File(instanceDir, PROPERTIES_FILENAME);
      propertiesFile.renameTo(new File(instanceDir, PROPERTIES_FILENAME + ".unloaded"));
      // This is a best-effort: the core.properties file may already have been
      // deleted by the core unload, so we don't worry about checking if the
      // rename has succeeded.
    }
  }

  @Override
  public void rename(CoreContainer cc, CoreDescriptor oldCD, CoreDescriptor newCD) {
    persist(cc, newCD);
  }

  @Override
  public List<CoreDescriptor> discover(CoreContainer cc) {
    logger.info("Looking for core definitions underneath {}", rootDirectory.getAbsolutePath());
    List<CoreDescriptor> cds = Lists.newArrayList();
    discoverUnder(rootDirectory, cds, cc);
    logger.info("Found {} core definitions", cds.size());
    return cds;
  }

  private void discoverUnder(File root, List<CoreDescriptor> cds, CoreContainer cc) {
    if (!root.exists())
      return;
    for (File child : root.listFiles()) {
      File propertiesFile = new File(child, PROPERTIES_FILENAME);
      if (propertiesFile.exists()) {
        CoreDescriptor cd = buildCoreDescriptor(propertiesFile, cc);
        logger.info("Found core {} in {}", cd.getName(), cd.getInstanceDir());
        cds.add(cd);
        continue;
      }
      if (child.isDirectory())
        discoverUnder(child, cds, cc);
    }
  }

  protected CoreDescriptor buildCoreDescriptor(File propertiesFile, CoreContainer cc) {
    try {
      File instanceDir = propertiesFile.getParentFile();
      Properties coreProperties = new Properties();
      coreProperties.load(new FileInputStream(propertiesFile));
      String name = createName(coreProperties, instanceDir);
      return new CoreDescriptor(cc, name, instanceDir.getAbsolutePath(), coreProperties);
    } catch (IOException e) {
      logger.error("Couldn't load core descriptor from {}:{}", propertiesFile.getAbsolutePath(), e.toString());
      return null;
    }
  }

  protected static String createName(Properties p, File instanceDir) {
    return p.getProperty(CoreDescriptor.CORE_NAME, instanceDir.getName());
  }

  protected Properties buildCoreProperties(CoreDescriptor cd) {
    Properties p = new Properties();
    p.putAll(cd.getPersistableStandardProperties());
    p.putAll(cd.getPersistableUserProperties());
    // We don't persist the instance directory, as that's defined by the location
    // of the properties file.
    p.remove(CoreDescriptor.CORE_INSTDIR);
    return p;
  }

}

@@ -831,48 +831,8 @@
 */%0A%0A
-import com.google.common.base.Charsets;%0A
 impo
@@ -1154,66 +1154,8 @@
 am;%0A
-import java.io.OutputStreamWriter;%0Aimport java.io.Writer;%0A
 impo
@@ -2865,74 +2865,8 @@
 e);%0A
-      Writer writer = new OutputStreamWriter(os, Charsets.UTF_8);%0A
     
@@ -2875,22 +2875,18 @@
 p.store(
-writer
+os
 , %22Writt
@@ -2937,30 +2937,8 @@
 ));%0A
-      writer.close();%0A
     

Use Properties.store(OutputStream, String) for back compatibility