/*
 * Copyright 2006-2010 the original author or authors.
 *
 * Licensed 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.
 */
package org.springframework.batch.core.configuration.support;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.Collection;
import java.util.Collections;

import org.junit.Test;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.JobParametersValidator;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.DuplicateJobException;
import org.springframework.batch.core.configuration.JobFactory;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.StepRegistry;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.step.NoSuchStepException;
import org.springframework.batch.core.step.StepLocator;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;

/**
 * @author Dave Syer
 * @author Stephane Nicoll
 */
public class DefaultJobLoaderTests {

	/**
     * The name of the job as defined in the test context used in this test.
     */
    private static final String TEST_JOB_NAME = "test-job";

    /**
     * The name of the step as defined in the test context used in this test.
     */
    private static final String TEST_STEP_NAME = "test-step";

    private JobRegistry jobRegistry = new MapJobRegistry();
    private StepRegistry stepRegistry = new MapStepRegistry();

    private DefaultJobLoader jobLoader = new DefaultJobLoader(jobRegistry, stepRegistry);

	@Test
	public void testLoadWithExplicitName() throws Exception {
		GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ByteArrayResource(
				JOB_XML.getBytes()));
		jobLoader.load(factory);
		assertEquals(1, jobRegistry.getJobNames().size());
		jobLoader.reload(factory);
		assertEquals(1, jobRegistry.getJobNames().size());
	}

    @Test
    public void createWithBothRegistries() {
        final DefaultJobLoader loader = new DefaultJobLoader();
        loader.setJobRegistry(jobRegistry);
        loader.setStepRegistry(stepRegistry);

        loader.afterPropertiesSet();
    }

    @Test
    public void createWithOnlyJobRegistry() {
        final DefaultJobLoader loader = new DefaultJobLoader();
        loader.setJobRegistry(jobRegistry);

        loader.afterPropertiesSet();
    }

    @Test
    public void testRegistryUpdated() throws DuplicateJobException {
    	GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
                new ClassPathResource("trivial-context.xml", getClass()));
        jobLoader.load(factory);
        assertEquals(1, jobRegistry.getJobNames().size());
        assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
    }

    @Test
    public void testMultipleJobsInTheSameContext() throws DuplicateJobException {
    	GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
                new ClassPathResource("job-context-with-steps.xml", getClass()));
        jobLoader.load(factory);
        assertEquals(2, jobRegistry.getJobNames().size());
        assertStepExist("job1", "step11", "step12");
        assertStepDoNotExist("job1", "step21", "step22");
        assertStepExist("job2", "step21", "step22");
        assertStepDoNotExist("job2", "step11", "step12");
    }

    @Test
    public void testMultipleJobsInTheSameContextWithSeparateSteps() throws DuplicateJobException {
    	GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
                new ClassPathResource("job-context-with-separate-steps.xml", getClass()));
        jobLoader.load(factory);
        assertEquals(2, jobRegistry.getJobNames().size());
        assertStepExist("job1", "step11", "step12", "genericStep1", "genericStep2");
        assertStepDoNotExist("job1", "step21", "step22");
        assertStepExist("job2", "step21", "step22", "genericStep1", "genericStep2");
        assertStepDoNotExist("job2", "step11", "step12");
    }

    @Test
    public void testNoStepRegistryAvailable() throws DuplicateJobException {
        final JobLoader loader = new DefaultJobLoader(jobRegistry);
        GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
                new ClassPathResource("job-context-with-steps.xml", getClass()));
        loader.load(factory);
        // No step registry available so just registering the jobs
        assertEquals(2, jobRegistry.getJobNames().size());
    }

    @Test
    public void testLoadWithJobThatIsNotAStepLocator() throws DuplicateJobException {
    	GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
                new ByteArrayResource(BASIC_JOB_XML.getBytes()));
        try {
            jobLoader.load(factory);
            fail("Should have failed with a ["+UnsupportedOperationException.class.getName()+"] as job does not" +
                    "implement StepLocator.");
        } catch (UnsupportedOperationException e) {
            // Job is not a step locator, can't register steps
        }

    }

    @Test
    public void testLoadWithJobThatIsNotAStepLocatorNoStepRegistry() throws DuplicateJobException {
        final JobLoader loader = new DefaultJobLoader(jobRegistry);
        GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
                new ByteArrayResource(BASIC_JOB_XML.getBytes()));
        try {
            loader.load(factory);
        } catch (UnsupportedOperationException e) {
            fail("Should not have failed with a [" + UnsupportedOperationException.class.getName() + "] as " +
                    "stepRegistry is not available for this JobLoader instance.");
        }
    }

    @Test
    public void testReload() throws Exception {
		GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ClassPathResource(
				"trivial-context.xml", getClass()));
        jobLoader.load(factory);
        assertEquals(1, jobRegistry.getJobNames().size());
        assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
        jobLoader.reload(factory);
        assertEquals(1, jobRegistry.getJobNames().size());
        assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
    }

    @Test
    public void testReloadWithAutoRegister() throws Exception {
		GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ClassPathResource(
				"trivial-context-autoregister.xml", getClass()));
        jobLoader.load(factory);
        assertEquals(1, jobRegistry.getJobNames().size());
        assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
        jobLoader.reload(factory);
        assertEquals(1, jobRegistry.getJobNames().size());
        assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
    }

    protected void assertStepExist(String jobName, String... stepNames) {
        for (String stepName : stepNames) {
            try {
                stepRegistry.getStep(jobName, stepName);
            } catch (NoSuchJobException e) {
                fail("Job with name [" + jobName + "] should have been found.");
            } catch (NoSuchStepException e) {
                fail("Step with name [" + stepName + "] for job [" + jobName + "] should have been found.");
            }
        }
    }

    protected void assertStepDoNotExist(String jobName, String... stepNames) {
        for (String stepName : stepNames) {
            try {
                final Step step = stepRegistry.getStep(jobName, stepName);
                fail("Step with name [" + stepName + "] for job [" + jobName + "] should " +
                        "not have been found but got [" + step + "]");
            } catch (NoSuchJobException e) {
                fail("Job with name [" + jobName + "] should have been found.");
            } catch (NoSuchStepException e) {
                // OK
            }
        }
    }

    private static final String BASIC_JOB_XML = String
            .format(
                    "<beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
                            + "xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd'><bean class='%s$BasicStubJob'/></beans>",
                    DefaultJobLoaderTests.class.getName());

    private static final String JOB_XML = String
            .format(
                    "<beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
                            + "xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd'><bean class='%s$StubJob'/></beans>",
                    DefaultJobLoaderTests.class.getName());

    public static class BasicStubJob implements Job {

        @Override
		public void execute(JobExecution execution) {
        }

        @Override
		public JobParametersIncrementer getJobParametersIncrementer() {
            return null;
        }

        @Override
		public String getName() {
            return "job";
        }

        @Override
		public boolean isRestartable() {
            return false;
        }

        @Override
		public JobParametersValidator getJobParametersValidator() {
            return null;
        }
    }

    public static class StubJob extends BasicStubJob implements StepLocator {

        @Override
		public Collection<String> getStepNames() {
            return Collections.emptyList();
        }

        @Override
		public Step getStep(String stepName) throws NoSuchStepException {
            throw new NoSuchStepException("Step [" + stepName + "] does not exist");
        }
    }

    private static class JobRegistryMock implements JobRegistry {
        @Override
		public void register(JobFactory jobFactory) throws DuplicateJobException {
            // dummy
        }

        @Override
		public void unregister(String jobName) {
            // dummy
        }

        @Override
		public Collection<String> getJobNames() {
            return Collections.emptyList();
        }

        @Override
		public Job getJob(String name) throws NoSuchJobException {
            throw new NoSuchJobException("Mock implementation does not hold any job.");
        }
    }
}
