/******************************************************************************* * 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. ******************************************************************************/ package org.apache.sling.scripting.sightly.impl.compiler; import java.io.InputStream; import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.io.IOUtils; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceMetadata; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.commons.classloader.ClassLoaderWriter; import org.apache.sling.commons.compiler.CompilationResult; import org.apache.sling.commons.compiler.CompilationUnit; import org.apache.sling.commons.compiler.CompilerMessage; import org.apache.sling.commons.compiler.JavaCompiler; import org.apache.sling.commons.compiler.Options; import org.apache.sling.scripting.api.resource.ScriptingResourceResolverProvider; import org.apache.sling.scripting.sightly.impl.engine.ResourceBackedPojoChangeMonitor; import org.apache.sling.scripting.sightly.impl.engine.SightlyEngineConfiguration; import org.apache.sling.scripting.sightly.impl.engine.SightlyJavaCompilerService; import org.apache.sling.scripting.sightly.impl.engine.compiled.SourceIdentifier; import org.apache.sling.scripting.sightly.impl.engine.runtime.RenderContextImpl; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.reflect.Whitebox; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; public class SightlyJavaCompilerServiceTest { private SightlyJavaCompilerService compiler; private ResourceBackedPojoChangeMonitor resourceBackedPojoChangeMonitor; private ClassLoaderWriter classLoaderWriter; private ScriptingResourceResolverProvider scriptingResourceResolverProvider; @Before public void setUp() throws Exception { compiler = spy(new SightlyJavaCompilerService()); resourceBackedPojoChangeMonitor = spy(new ResourceBackedPojoChangeMonitor()); SightlyEngineConfiguration sightlyEngineConfiguration = mock(SightlyEngineConfiguration.class); when(sightlyEngineConfiguration.getBundleSymbolicName()).thenReturn("org.apache.sling.scripting.sightly"); when(sightlyEngineConfiguration.getScratchFolder()).thenReturn("/org/apache/sling/scripting/sightly"); Whitebox.setInternalState(compiler, "sightlyEngineConfiguration", sightlyEngineConfiguration); Whitebox.setInternalState(compiler, "resourceBackedPojoChangeMonitor", resourceBackedPojoChangeMonitor); classLoaderWriter = Mockito.mock(ClassLoaderWriter.class); ClassLoader classLoader = Mockito.mock(ClassLoader.class); when(classLoaderWriter.getClassLoader()).thenReturn(classLoader); } @After public void tearDown() throws Exception { compiler = null; resourceBackedPojoChangeMonitor = null; classLoaderWriter = null; } @Test public void testDiskCachedUseObject() throws Exception { String pojoPath = "/apps/myproject/testcomponents/a/Pojo.java"; String className = "apps.myproject.testcomponents.a.Pojo"; scriptingResourceResolverProvider = Mockito.mock(ScriptingResourceResolverProvider.class); ResourceResolver resolver = Mockito.mock(ResourceResolver.class); when(scriptingResourceResolverProvider.getRequestScopedResourceResolver()).thenReturn(resolver); Resource pojoResource = Mockito.mock(Resource.class); when(pojoResource.getPath()).thenReturn(pojoPath); ResourceMetadata mockMetadata = Mockito.mock(ResourceMetadata.class); when(mockMetadata.getModificationTime()).thenReturn(1l); when(pojoResource.getResourceMetadata()).thenReturn(mockMetadata); when(pojoResource.adaptTo(InputStream.class)).thenReturn(IOUtils.toInputStream("DUMMY", "UTF-8")); when(resolver.getResource(pojoPath)).thenReturn(pojoResource); when(classLoaderWriter.getLastModified("/apps/myproject/testcomponents/a/Pojo.class")).thenReturn(2l); getInstancePojoTest(className); /* * assuming the compiled class has a last modified date greater than the source, then the compiler should not recompile the Use * object */ verify(compiler, never()).compileSource(any(SourceIdentifier.class), anyString()); } @Test public void testObsoleteDiskCachedUseObject() throws Exception { String pojoPath = "/apps/myproject/testcomponents/a/Pojo.java"; String className = "apps.myproject.testcomponents.a.Pojo"; scriptingResourceResolverProvider = Mockito.mock(ScriptingResourceResolverProvider.class); ResourceResolver resolver = Mockito.mock(ResourceResolver.class); when(scriptingResourceResolverProvider.getRequestScopedResourceResolver()).thenReturn(resolver); Resource pojoResource = Mockito.mock(Resource.class); when(pojoResource.getPath()).thenReturn(pojoPath); ResourceMetadata mockMetadata = Mockito.mock(ResourceMetadata.class); when(mockMetadata.getModificationTime()).thenReturn(2l); when(pojoResource.getResourceMetadata()).thenReturn(mockMetadata); when(pojoResource.adaptTo(InputStream.class)).thenReturn(IOUtils.toInputStream("DUMMY", "UTF-8")); when(resolver.getResource(pojoPath)).thenReturn(pojoResource); when(classLoaderWriter.getLastModified("/apps/myproject/testcomponents/a/Pojo.class")).thenReturn(1l); getInstancePojoTest(className); /* * assuming the compiled class has a last modified date greater than the source, then the compiler should not recompile the Use * object */ verify(compiler).compileSource(any(SourceIdentifier.class), anyString()); } @Test public void testMemoryCachedUseObject() throws Exception { String pojoPath = "/apps/myproject/testcomponents/a/Pojo.java"; String className = "apps.myproject.testcomponents.a.Pojo"; scriptingResourceResolverProvider = Mockito.mock(ScriptingResourceResolverProvider.class); ResourceResolver resolver = Mockito.mock(ResourceResolver.class); when(scriptingResourceResolverProvider.getRequestScopedResourceResolver()).thenReturn(resolver); Resource pojoResource = Mockito.mock(Resource.class); when(pojoResource.getPath()).thenReturn(pojoPath); when(resourceBackedPojoChangeMonitor.getLastModifiedDateForJavaUseObject(pojoPath)).thenReturn(1l); when(pojoResource.adaptTo(InputStream.class)).thenReturn(IOUtils.toInputStream("DUMMY", "UTF-8")); when(resolver.getResource(pojoPath)).thenReturn(pojoResource); when(classLoaderWriter.getLastModified("/apps/myproject/testcomponents/a/Pojo.class")).thenReturn(2l); getInstancePojoTest(className); /* * assuming the compiled class has a last modified date greater than the source, then the compiler should not recompile the Use * object */ verify(compiler, never()).compileSource(any(SourceIdentifier.class), anyString()); } @Test public void testObsoleteMemoryCachedUseObject() throws Exception { String pojoPath = "/apps/myproject/testcomponents/a/Pojo.java"; String className = "apps.myproject.testcomponents.a.Pojo"; scriptingResourceResolverProvider = Mockito.mock(ScriptingResourceResolverProvider.class); ResourceResolver resolver = Mockito.mock(ResourceResolver.class); when(scriptingResourceResolverProvider.getRequestScopedResourceResolver()).thenReturn(resolver); Resource pojoResource = Mockito.mock(Resource.class); when(pojoResource.getPath()).thenReturn(pojoPath); when(resourceBackedPojoChangeMonitor.getLastModifiedDateForJavaUseObject(pojoPath)).thenReturn(2l); when(pojoResource.adaptTo(InputStream.class)).thenReturn(IOUtils.toInputStream("DUMMY", "UTF-8")); when(resolver.getResource(pojoPath)).thenReturn(pojoResource); when(classLoaderWriter.getLastModified("/apps/myproject/testcomponents/a/Pojo.class")).thenReturn(1l); getInstancePojoTest(className); /* * assuming the compiled class has a last modified date greater than the source, then the compiler should not recompile the Use * object */ verify(compiler).compileSource(any(SourceIdentifier.class), anyString()); } private void getInstancePojoTest(String className) throws Exception { RenderContextImpl renderContext = Mockito.mock(RenderContextImpl.class); JavaCompiler javaCompiler = Mockito.mock(JavaCompiler.class); CompilationResult compilationResult = Mockito.mock(CompilationResult.class); when(compilationResult.getErrors()).thenReturn(new ArrayList<CompilerMessage>()); when(javaCompiler.compile(Mockito.any(CompilationUnit[].class), Mockito.any(Options.class))).thenReturn(compilationResult); when(classLoaderWriter.getClassLoader().loadClass(className)).thenAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return MockPojo.class; } }); Whitebox.setInternalState(compiler, "classLoaderWriter", classLoaderWriter); Whitebox.setInternalState(compiler, "javaCompiler", javaCompiler); Whitebox.setInternalState(compiler, "scriptingResourceResolverProvider", scriptingResourceResolverProvider); Object obj = compiler.getResourceBackedUseObject(renderContext, className); assertTrue("Expected to obtain a " + MockPojo.class.getName() + " object.", obj instanceof MockPojo); } }