/*
 * Copyright (C) 2009 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.exoplatform.services.jcr.impl.storage.value.fs;

import org.exoplatform.commons.utils.PrivilegedFileHelper;
import org.exoplatform.services.jcr.BaseStandaloneTest;
import org.exoplatform.services.jcr.config.WorkspaceEntry;
import org.exoplatform.services.jcr.core.WorkspaceContainerFacade;
import org.exoplatform.services.jcr.datamodel.PropertyData;
import org.exoplatform.services.jcr.impl.core.PropertyImpl;
import org.exoplatform.services.jcr.impl.core.SessionImpl;
import org.exoplatform.services.jcr.impl.dataflow.SpoolConfig;
import org.exoplatform.services.jcr.storage.value.ValueIOChannel;
import org.exoplatform.services.jcr.storage.value.ValueStoragePluginProvider;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;

/**
 * Created by The eXo Platform SAS. <br/>
 * Test to reproduce bug when values from value storages are not removed in some
 * cases.
 * 
 * @author Nikolay Zamosenchuk
 * @version $Id$
 */
public class TestRemoveFromValueStorage extends BaseStandaloneTest
{

   private Node testRoot;

   private Property prop = null;

   private Value[] values;

   private int largeCount = 5;

   private int smallCount = 5;

   private int largeValueSize = 1024 * 1024 * 2;

   private int smallValueSize = 1000 * 1024;

   private SessionImpl mySession;

   private Node myRoot;

   @Override
   public void setUp() throws Exception
   {
      super.setUp();
      // This test uses special workspace ("ws3"), with complex value storage. So
      // we need to login into another workspace.
      mySession = (SessionImpl)repository.login(credentials, "ws3");
      myRoot = mySession.getRootNode();

      // creating property with binary values.
      testRoot = myRoot.addNode("TestRoot");

      values = new Value[largeCount + smallCount];

      // creating large one's
      for (int i = 0; i < largeCount; i++)
      {
         // 2M will be stored in first value storage
         byte[] largeValue = new byte[largeValueSize];
         Random generator = new Random();
         generator.nextBytes(largeValue);
         values[smallCount + i] =
            testRoot.getSession().getValueFactory().createValue(new ByteArrayInputStream(largeValue));
      }

      // creating small one's
      for (int i = 0; i < smallCount; i++)
      {
         // 1000K will be stored in second value storage
         byte[] smallValue = new byte[smallValueSize];
         Random generator = new Random();
         generator.nextBytes(smallValue);
         values[i] = testRoot.getSession().getValueFactory().createValue(new ByteArrayInputStream(smallValue));
      }

      if (values.length == 1)
      {
         prop = testRoot.setProperty("binaryProperty", values[0]);
      }
      else
      {
         prop = testRoot.setProperty("binaryProperty", values);
      }
      mySession.save();
   }

   public void testRemoveValue() throws Exception
   {
      WorkspaceContainerFacade containerFacade = repository.getWorkspaceContainer(mySession.getWorkspace().getName());
      WorkspaceEntry wsConfig = (WorkspaceEntry)containerFacade.getComponent(WorkspaceEntry.class);

      if (wsConfig.getContainer().getValueStorages() != null && wsConfig.getContainer().getValueStorages().size() > 0)
      {

         // reading values directly from value storage
         PropertyImpl propertyImpl = (PropertyImpl)prop;
         ValueStoragePluginProvider storageProvider =
            (ValueStoragePluginProvider)containerFacade.getComponent(ValueStoragePluginProvider.class);

         String propertyId = propertyImpl.getInternalIdentifier();
         int count = prop.getValues().length;
         Map<Integer, FileIOChannel> channels = new HashMap<Integer, FileIOChannel>();

         for (int i = 0; i < count; i++)
         {
            ValueIOChannel channel = storageProvider.getApplicableChannel((PropertyData)propertyImpl.getData(), i);
            if (channel != null)
            {
               channels.put(i, (FileIOChannel)channel);
            }
         }

         for (int i = 0; i < channels.size(); i++)
         {
            try
            {
               channels.get(i).read(propertyId, i, PropertyType.BINARY, SpoolConfig.getDefaultSpoolConfig());
            }
            catch (IOException e)
            {
               fail("Poperty value " + i + " can't be read!");
            }
         }

         prop.remove();
         mySession.save();

         // checking whether values are still in value storage.
         for (int i = 0; i < channels.size(); i++)
         {
            try
            {
               // TreeFileIOChannel always returns a File. But if this file doesn't
               // really exists is size is 0.
               File value = channels.get(i).getFile(propertyId, i);
               if (value.length() != 0)
               {
                  fail("Poperty value still can be found in value-storage but should have been already deleted!");
               }
            }
            catch (IOException e)
            {
               // ok
            }
         }
      }
      else
      {
         log.warn("Value storages are not configured, test skipped.");
      }
   }

   public void testRemoveInconsistentNode() throws Exception
   {
      Property BinaryProp = null;
      WorkspaceContainerFacade containerFacade = repository.getWorkspaceContainer(myRoot.getSession().getWorkspace().getName());
      WorkspaceEntry wsConfig = (WorkspaceEntry)containerFacade.getComponent(WorkspaceEntry.class);
      String vsPath = wsConfig.getContainer().getValueStorages().get(0)
            .getParameterValue(FileValueStorage.PATH);

      Node myNode = myRoot.addNode("myNode");
      values = new Value[1];


         byte[] largeValue = new byte[largeValueSize];
         Random generator = new Random();
         generator.nextBytes(largeValue);
         values[0] =
            myNode.getSession().getValueFactory().createValue(new ByteArrayInputStream(largeValue));

      BinaryProp = myNode.setProperty("binaryProperty", values);
      myRoot.save();
      PropertyImpl propertyImpl = (PropertyImpl)BinaryProp;

      ValueStoragePluginProvider storageProvider =
         (ValueStoragePluginProvider)containerFacade.getComponent(ValueStoragePluginProvider.class);
      ValueIOChannel channel = storageProvider.getApplicableChannel((PropertyData)propertyImpl.getData(), 0);
      String filePath=((FileIOChannel)channel).makeFilePath(propertyImpl.getInternalIdentifier(), 0);

      File folder =new File(vsPath+"/"+filePath).getParentFile();
      File[] list =folder.listFiles();
      for(File f : list)
      {
         PrivilegedFileHelper.delete(f);
      }
      PrivilegedFileHelper.delete(folder);
      try
      {
         myNode.remove();
         myRoot.save();
      }
      catch (RepositoryException exp)
      {
         fail(exp.getMessage());
      }

   }

   @Override
   protected void tearDown() throws Exception
   {
      if (mySession != null)
      {
         testRoot.remove();
         mySession.logout();
      }
      super.tearDown();
   }

   @Override
   protected String getRepositoryName()
   {
      return repository.getName();
   }

}
