Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java ------------------------------------------------------------------------------ svn:eol-style = native
Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/SlingContextImpl.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,23 @@ +/* + * 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. + */ +/** + * Sling context implementation for unit tests. + */ +package org.apache.sling.testing.mock.sling.context; + Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/context/package-info.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContext.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContext.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContext.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContext.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,207 @@ +/* + * 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.testing.mock.sling.junit; + +import org.apache.sling.testing.mock.sling.ResourceResolverType; +import org.apache.sling.testing.mock.sling.context.SlingContextImpl; +import org.junit.rules.ExternalResource; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * JUnit rule for setting up and tearing down Sling context objects for unit + * tests. + */ +public final class SlingContext extends SlingContextImpl implements TestRule { + + private final SlingContextCallback setUpCallback; + private final SlingContextCallback tearDownCallback; + private final ResourceResolverType resourceResolverType; + private final TestRule delegate; + + /** + * Initialize Sling context. + * <p> + * If context is initialized with: + * </p> + * <ul> + * <li>No resource resolver type - default is used + * {@link MockSling#DEFAULT_RESOURCERESOLVER_TYPE}.</li> + * <li>One resource resolver type - exactly this is used.</li> + * <li>More than one: all unit test methods are executed for all resource + * resolver types using {@link ListGenerator}.</li> + * </ul> + */ + public SlingContext() { + this(null, null, null); + } + + /** + * Initialize Sling context. + * <p> + * If context is initialized with: + * </p> + * <ul> + * <li>No resource resolver type - default is used + * {@link MockSling#DEFAULT_RESOURCERESOLVER_TYPE}.</li> + * <li>One resource resolver type - exactly this is used.</li> + * <li>More than one: all unit test methods are executed for all resource + * resolver types using {@link ListGenerator}.</li> + * </ul> + * @param resourceResolverType Resource resolver type. + */ + public SlingContext(final ResourceResolverType resourceResolverType) { + this(null, null, resourceResolverType); + } + + /** + * Initialize Sling context. + * <p> + * If context is initialized with: + * </p> + * <ul> + * <li>No resource resolver type - default is used + * {@link MockSling#DEFAULT_RESOURCERESOLVER_TYPE}.</li> + * <li>One resource resolver type - exactly this is used.</li> + * <li>More than one: all unit test methods are executed for all resource + * resolver types using {@link ListGenerator}.</li> + * </ul> + * @param setUpCallback Allows the application to register an own callback + * function that is called after the built-in setup rules are + * executed. + */ + public SlingContext(final SlingContextCallback setUpCallback) { + this(setUpCallback, null, null); + } + + /** + * Initialize Sling context. + * <p> + * If context is initialized with: + * </p> + * <ul> + * <li>No resource resolver type - default is used + * {@link MockSling#DEFAULT_RESOURCERESOLVER_TYPE}.</li> + * <li>One resource resolver type - exactly this is used.</li> + * <li>More than one: all unit test methods are executed for all resource + * resolver types using {@link ListGenerator}.</li> + * </ul> + * @param setUpCallback Allows the application to register an own callback + * function that is called after the built-in setup rules are + * executed. + * @param resourceResolverType Resource resolver type. + */ + public SlingContext(final SlingContextCallback setUpCallback, final ResourceResolverType resourceResolverType) { + this(setUpCallback, null, resourceResolverType); + } + + /** + * Initialize Sling context. + * <p> + * If context is initialized with: + * </p> + * <ul> + * <li>No resource resolver type - default is used + * {@link MockSling#DEFAULT_RESOURCERESOLVER_TYPE}.</li> + * <li>One resource resolver type - exactly this is used.</li> + * <li>More than one: all unit test methods are executed for all resource + * resolver types using {@link ListGenerator}.</li> + * </ul> + * @param setUpCallback Allows the application to register an own callback + * function that is called after the built-in setup rules are + * executed. + * @param tearDownCallback Allows the application to register an own + * callback function that is called before the built-in teardown + * rules are executed. + */ + public SlingContext(final SlingContextCallback setUpCallback, final SlingContextCallback tearDownCallback) { + this(setUpCallback, tearDownCallback, null); + } + + /** + * Initialize Sling context. + * <p> + * If context is initialized with: + * </p> + * <ul> + * <li>No resource resolver type - default is used + * {@link MockSling#DEFAULT_RESOURCERESOLVER_TYPE}.</li> + * <li>One resource resolver type - exactly this is used.</li> + * <li>More than one: all unit test methods are executed for all resource + * resolver types using {@link ListGenerator}.</li> + * </ul> + * @param setUpCallback Allows the application to register an own callback + * function that is called after the built-in setup rules are + * executed. + * @param tearDownCallback Allows the application to register an own + * callback function that is called before the built-in teardown + * rules are executed. + * @param resourceResolverType Resource resolver type. + */ + public SlingContext(final SlingContextCallback setUpCallback, final SlingContextCallback tearDownCallback, + final ResourceResolverType resourceResolverType) { + + this.setUpCallback = setUpCallback; + this.tearDownCallback = tearDownCallback; + this.resourceResolverType = resourceResolverType; + + // user default rule that directly executes each test method once + setResourceResolverType(this.resourceResolverType); + this.delegate = new ExternalResource() { + @Override + protected void before() { + SlingContext.this.setUp(); + SlingContext.this.executeSetUpCallback(); + } + + @Override + protected void after() { + SlingContext.this.executeTearDownCallback(); + SlingContext.this.tearDown(); + } + }; + } + + @Override + public Statement apply(final Statement base, final Description description) { + return this.delegate.apply(base, description); + } + + private void executeSetUpCallback() { + if (this.setUpCallback != null) { + try { + this.setUpCallback.execute(this); + } catch (Throwable ex) { + throw new RuntimeException("Executing setup callback failed.", ex); + } + } + } + + private void executeTearDownCallback() { + if (this.tearDownCallback != null) { + try { + this.tearDownCallback.execute(this); + } catch (Throwable ex) { + throw new RuntimeException("Executing setup callback failed.", ex); + } + } + } + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContext.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContext.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContext.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContextCallback.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContextCallback.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContextCallback.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContextCallback.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,37 @@ +/* + * 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.testing.mock.sling.junit; + +import java.io.IOException; + +import org.apache.sling.api.resource.PersistenceException; + +/** + * Callback-interface for application-specific setup and teardown operations to + * customize the {@link AemContext} JUnit rule. + */ +public interface SlingContextCallback { + + /** + * Execute callback action + * @param context Sling context + */ + void execute(SlingContext context) throws IOException, PersistenceException; + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContextCallback.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContextCallback.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/SlingContextCallback.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/package-info.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/package-info.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/package-info.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/package-info.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,23 @@ +/* + * 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. + */ +/** + * Rule for providing easy access to Sling context in JUnit tests. + */ +package org.apache.sling.testing.mock.sling.junit; + Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/package-info.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/package-info.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/junit/package-info.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,586 @@ +/* + * 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.testing.mock.sling.loader; + +import java.io.IOException; +import java.io.InputStream; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.jackrabbit.JcrConstants; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceUtil; +import org.apache.sling.commons.json.JSONArray; +import org.apache.sling.commons.json.JSONException; +import org.apache.sling.commons.json.JSONObject; +import org.apache.sling.commons.json.jcr.JsonItemWriter; +import org.apache.sling.commons.mime.MimeTypeService; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +/** + * Imports JSON data and binary data into Sling resource hierarchy. + */ +public final class ContentLoader { + + private static final String REFERENCE = "jcr:reference:"; + private static final String PATH = "jcr:path:"; + private static final String CONTENTTYPE_OCTET_STREAM = "application/octet-stream"; + + private static final Set<String> IGNORED_NAMES = ImmutableSet.of(JcrConstants.JCR_PRIMARYTYPE, + JcrConstants.JCR_MIXINTYPES, JcrConstants.JCR_UUID, JcrConstants.JCR_BASEVERSION, + JcrConstants.JCR_PREDECESSORS, JcrConstants.JCR_SUCCESSORS, JcrConstants.JCR_CREATED, "jcr:checkedOut"); + + private final ResourceResolver resourceResolver; + private final BundleContext bundleContext; + private final DateFormat calendarFormat; + + /** + * @param resourceResolver Resource resolver + */ + public ContentLoader(ResourceResolver resourceResolver) { + this(resourceResolver, null); + } + + /** + * @param resourceResolver Resource resolver + * @param bundleContext Bundle context + */ + public ContentLoader(ResourceResolver resourceResolver, BundleContext bundleContext) { + this.resourceResolver = resourceResolver; + this.bundleContext = bundleContext; + this.calendarFormat = new SimpleDateFormat(JsonItemWriter.ECMA_DATE_FORMAT, JsonItemWriter.DATE_FORMAT_LOCALE); + } + + /** + * Import content of JSON file into repository. + * @param classpathResource Classpath resource URL for JSON content + * @param parentResource Parent resource + * @param childName Name of child resource to create with JSON content + * @return Resource + */ + public Resource json(String classpathResource, Resource parentResource, String childName) { + InputStream is = ContentLoader.class.getResourceAsStream(classpathResource); + if (is == null) { + throw new IllegalArgumentException("Classpath resource not found: " + classpathResource); + } + try { + return json(is, parentResource, childName); + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } + + /** + * Import content of JSON file into repository. Auto-creates parent + * hierarchies as nt:unstrucured nodes if missing. + * @param classpathResource Classpath resource URL for JSON content + * @param destPath Path to import the JSON content to + * @return Resource + */ + public Resource json(String classpathResource, String destPath) { + InputStream is = ContentLoader.class.getResourceAsStream(classpathResource); + if (is == null) { + throw new IllegalArgumentException("Classpath resource not found: " + classpathResource); + } + try { + return json(is, destPath); + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } + + /** + * Import content of JSON file into repository. + * @param inputStream Input stream with JSON content + * @param parentResource Parent resource + * @param childName Name of child resource to create with JSON content + * @return Resource + */ + public Resource json(InputStream inputStream, Resource parentResource, String childName) { + return json(inputStream, parentResource.getPath() + "/" + childName); + } + + /** + * Import content of JSON file into repository. Auto-creates parent + * hierarchies as nt:unstrucured nodes if missing. + * @param inputStream Input stream with JSON content + * @param destPath Path to import the JSON content to + * @return Resource + */ + public Resource json(InputStream inputStream, String destPath) { + try { + String parentPath = ResourceUtil.getParent(destPath); + String childName = ResourceUtil.getName(destPath); + + Resource parentResource = resourceResolver.getResource(parentPath); + if (parentResource == null) { + parentResource = createResourceHierarchy(parentPath); + } + if (parentResource.getChild(childName) != null) { + throw new IllegalArgumentException("Resource does already exist: " + destPath); + } + + String jsonString = convertToJsonString(inputStream).trim(); + JSONObject json = new JSONObject(jsonString); + return this.createResource(parentResource, childName, json); + } catch (JSONException ex) { + throw new RuntimeException(ex); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private Resource createResourceHierarchy(String path) { + String parentPath = ResourceUtil.getParent(path); + if (parentPath == null) { + return null; + } + Resource parentResource = resourceResolver.getResource(parentPath); + if (parentResource == null) { + parentResource = createResourceHierarchy(parentPath); + } + Map<String, Object> props = new HashMap<String, Object>(); + props.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED); + try { + return resourceResolver.create(parentResource, ResourceUtil.getName(path), props); + } catch (PersistenceException ex) { + throw new RuntimeException(ex); + } + } + + private Resource createResource(Resource parentResource, String childName, JSONObject jsonObject) + throws IOException, JSONException { + + // collect all properties first + Map<String, Object> props = new HashMap<String, Object>(); + JSONArray names = jsonObject.names(); + for (int i = 0; names != null && i < names.length(); i++) { + final String name = names.getString(i); + if (!IGNORED_NAMES.contains(name)) { + Object obj = jsonObject.get(name); + if (!(obj instanceof JSONObject)) { + this.setProperty(props, name, obj); + } + } + } + + // validate JCR primary type + Object primaryTypeObj = jsonObject.opt(JcrConstants.JCR_PRIMARYTYPE); + String primaryType = null; + if (primaryTypeObj != null) { + primaryType = String.valueOf(primaryTypeObj); + } + if (primaryType == null) { + primaryType = JcrConstants.NT_UNSTRUCTURED; + } + props.put(JcrConstants.JCR_PRIMARYTYPE, primaryType); + + // create resource + Resource resource = resourceResolver.create(parentResource, childName, props); + + // add child resources + for (int i = 0; names != null && i < names.length(); i++) { + final String name = names.getString(i); + if (!IGNORED_NAMES.contains(name)) { + Object obj = jsonObject.get(name); + if (obj instanceof JSONObject) { + createResource(resource, name, (JSONObject) obj); + } + } + } + + return resource; + } + + private void setProperty(Map<String, Object> props, String name, Object value) throws JSONException { + if (value instanceof JSONArray) { + // multivalue + final JSONArray array = (JSONArray) value; + if (array.length() > 0) { + final Object[] values = new Object[array.length()]; + for (int i = 0; i < array.length(); i++) { + values[i] = array.get(i); + } + + if (values[0] instanceof Double || values[0] instanceof Float) { + Double[] arrayValues = new Double[values.length]; + for (int i = 0; i < values.length; i++) { + arrayValues[i] = (Double) values[i]; + } + props.put(cleanupJsonName(name), arrayValues); + } else if (values[0] instanceof Number) { + Long[] arrayValues = new Long[values.length]; + for (int i = 0; i < values.length; i++) { + arrayValues[i] = ((Number) values[i]).longValue(); + } + props.put(cleanupJsonName(name), arrayValues); + } else if (values[0] instanceof Boolean) { + Boolean[] arrayValues = new Boolean[values.length]; + for (int i = 0; i < values.length; i++) { + arrayValues[i] = (Boolean) values[i]; + } + props.put(cleanupJsonName(name), arrayValues); + } else { + String[] arrayValues = new String[values.length]; + for (int i = 0; i < values.length; i++) { + arrayValues[i] = values[i].toString(); + } + props.put(cleanupJsonName(name), arrayValues); + } + } else { + props.put(cleanupJsonName(name), new String[0]); + } + + } else { + // single value + if (value instanceof Double || value instanceof Float) { + props.put(cleanupJsonName(name), value); + } else if (value instanceof Number) { + props.put(cleanupJsonName(name), ((Number) value).longValue()); + } else if (value instanceof Boolean) { + props.put(cleanupJsonName(name), value); + } else { + String stringValue = value.toString(); + + // check if value is a Calendar object + Calendar calendar = tryParseCalendarValue(stringValue); + if (calendar != null) { + props.put(cleanupJsonName(name), calendar); + } else { + props.put(cleanupJsonName(name), stringValue); + } + + } + } + } + + private String cleanupJsonName(String name) { + if (name.startsWith(REFERENCE)) { + return name.substring(REFERENCE.length()); + } + if (name.startsWith(PATH)) { + return name.substring(PATH.length()); + } + return name; + } + + private String convertToJsonString(InputStream inputStream) { + try { + return IOUtils.toString(inputStream); + } catch (IOException ex) { + throw new RuntimeException(ex); + } finally { + try { + inputStream.close(); + } catch (IOException ex) { + // ignore + } + } + } + + private Calendar tryParseCalendarValue(String value) { + if (StringUtils.isNotBlank(value)) { + synchronized (calendarFormat) { + try { + Date date = calendarFormat.parse(value); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar; + } catch (ParseException ex) { + // ignore + } + } + } + return null; + } + + /** + * Import binary file as nt:file binary node into repository. Auto-creates + * parent hierarchies as nt:unstrucured nodes if missing. Mime type is + * auto-detected from resource name. + * @param classpathResource Classpath resource URL for binary file. + * @param path Path to mount binary data to (parent nodes created + * automatically) + * @return Resource with binary data + */ + public Resource binaryFile(String classpathResource, String path) { + InputStream is = ContentLoader.class.getResourceAsStream(classpathResource); + if (is == null) { + throw new IllegalArgumentException("Classpath resource not found: " + classpathResource); + } + try { + return binaryFile(is, path, detectMimeTypeFromName(path)); + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } + + /** + * Import binary file as nt:file binary node into repository. Auto-creates + * parent hierarchies as nt:unstrucured nodes if missing. + * @param classpathResource Classpath resource URL for binary file. + * @param path Path to mount binary data to (parent nodes created + * automatically) + * @param mimeType Mime type of binary data + * @return Resource with binary data + */ + public Resource binaryFile(String classpathResource, String path, String mimeType) { + InputStream is = ContentLoader.class.getResourceAsStream(classpathResource); + if (is == null) { + throw new IllegalArgumentException("Classpath resource not found: " + classpathResource); + } + try { + return binaryFile(is, path, mimeType); + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } + + /** + * Import binary file as nt:file binary node into repository. Auto-creates + * parent hierarchies as nt:unstrucured nodes if missing. Mime type is + * auto-detected from resource name. + * @param inputStream Input stream for binary data + * @param path Path to mount binary data to (parent nodes created + * automatically) + * @return Resource with binary data + */ + public Resource binaryFile(InputStream inputStream, String path) { + return binaryFile(inputStream, path, detectMimeTypeFromName(path)); + } + + /** + * Import binary file as nt:file binary node into repository. Auto-creates + * parent hierarchies as nt:unstrucured nodes if missing. + * @param inputStream Input stream for binary data + * @param path Path to mount binary data to (parent nodes created + * automatically) + * @param mimeType Mime type of binary data + * @return Resource with binary data + */ + public Resource binaryFile(InputStream inputStream, String path, String mimeType) { + String parentPath = ResourceUtil.getParent(path, 1); + String name = ResourceUtil.getName(path); + Resource parentResource = resourceResolver.getResource(parentPath); + if (parentResource == null) { + parentResource = createResourceHierarchy(parentPath); + } + return binaryFile(inputStream, parentResource, name, mimeType); + } + + /** + * Import binary file as nt:file binary node into repository. Auto-creates + * parent hierarchies as nt:unstrucured nodes if missing. Mime type is + * auto-detected from resource name. + * @param inputStream Input stream for binary data + * @param parentResource Parent resource + * @param name Resource name for nt:file + * @return Resource with binary data + */ + public Resource binaryFile(InputStream inputStream, Resource parentResource, String name) { + return binaryFile(inputStream, parentResource, name, detectMimeTypeFromName(name)); + } + + /** + * Import binary file as nt:file binary node into repository. Auto-creates + * parent hierarchies as nt:unstrucured nodes if missing. + * @param inputStream Input stream for binary data + * @param parentResource Parent resource + * @param name Resource name for nt:file + * @param mimeType Mime type of binary data + * @return Resource with binary data + */ + public Resource binaryFile(InputStream inputStream, Resource parentResource, String name, String mimeType) { + try { + Resource file = resourceResolver.create(parentResource, name, + ImmutableMap.<String, Object> builder().put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_FILE) + .build()); + resourceResolver.create(file, JcrConstants.JCR_CONTENT, + ImmutableMap.<String, Object> builder().put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_RESOURCE) + .put(JcrConstants.JCR_DATA, inputStream).put(JcrConstants.JCR_MIMETYPE, mimeType).build()); + return file; + } catch (PersistenceException ex) { + throw new RuntimeException("Unable to create resource at " + parentResource.getPath() + "/" + name, ex); + } + } + + /** + * Import binary file as nt:resource binary node into repository. + * Auto-creates parent hierarchies as nt:unstrucured nodes if missing. Mime + * type is auto-detected from resource name. + * @param classpathResource Classpath resource URL for binary file. + * @param path Path to mount binary data to (parent nodes created + * automatically) + * @return Resource with binary data + */ + public Resource binaryResource(String classpathResource, String path) { + InputStream is = ContentLoader.class.getResourceAsStream(classpathResource); + if (is == null) { + throw new IllegalArgumentException("Classpath resource not found: " + classpathResource); + } + try { + return binaryResource(is, path, detectMimeTypeFromName(path)); + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } + + /** + * Import binary file as nt:resource binary node into repository. + * Auto-creates parent hierarchies as nt:unstrucured nodes if missing. + * @param classpathResource Classpath resource URL for binary file. + * @param path Path to mount binary data to (parent nodes created + * automatically) + * @param mimeType Mime type of binary data + * @return Resource with binary data + */ + public Resource binaryResource(String classpathResource, String path, String mimeType) { + InputStream is = ContentLoader.class.getResourceAsStream(classpathResource); + if (is == null) { + throw new IllegalArgumentException("Classpath resource not found: " + classpathResource); + } + try { + return binaryResource(is, path, mimeType); + } finally { + try { + is.close(); + } catch (IOException ex) { + // ignore + } + } + } + + /** + * Import binary file as nt:resource binary node into repository. + * Auto-creates parent hierarchies as nt:unstrucured nodes if missing. Mime + * type is auto-detected from resource name. + * @param inputStream Input stream for binary data + * @param path Path to mount binary data to (parent nodes created + * automatically) + * @return Resource with binary data + */ + public Resource binaryResource(InputStream inputStream, String path) { + return binaryResource(inputStream, path, detectMimeTypeFromName(path)); + } + + /** + * Import binary file as nt:resource binary node into repository. + * Auto-creates parent hierarchies as nt:unstrucured nodes if missing. + * @param inputStream Input stream for binary data + * @param path Path to mount binary data to (parent nodes created + * automatically) + * @param mimeType Mime type of binary data + * @return Resource with binary data + */ + public Resource binaryResource(InputStream inputStream, String path, String mimeType) { + String parentPath = ResourceUtil.getParent(path, 1); + String name = ResourceUtil.getName(path); + Resource parentResource = resourceResolver.getResource(parentPath); + if (parentResource == null) { + parentResource = createResourceHierarchy(parentPath); + } + return binaryResource(inputStream, parentResource, name, mimeType); + } + + /** + * Import binary file as nt:resource binary node into repository. + * Auto-creates parent hierarchies as nt:unstrucured nodes if missing. Mime + * type is auto-detected from resource name. + * @param inputStream Input stream for binary data + * @param parentResource Parent resource + * @param name Resource name for nt:resource + * @return Resource with binary data + */ + public Resource binaryResource(InputStream inputStream, Resource parentResource, String name) { + return binaryResource(inputStream, parentResource, name, detectMimeTypeFromName(name)); + } + + /** + * Import binary file as nt:resource binary node into repository. + * Auto-creates parent hierarchies as nt:unstrucured nodes if missing. + * @param inputStream Input stream for binary data + * @param parentResource Parent resource + * @param name Resource name for nt:resource + * @param mimeType Mime type of binary data + * @return Resource with binary data + */ + public Resource binaryResource(InputStream inputStream, Resource parentResource, String name, String mimeType) { + try { + return resourceResolver.create(parentResource, name, + ImmutableMap.<String, Object> builder().put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_RESOURCE) + .put(JcrConstants.JCR_DATA, inputStream).put(JcrConstants.JCR_MIMETYPE, mimeType).build()); + } catch (PersistenceException ex) { + throw new RuntimeException("Unable to create resource at " + parentResource.getPath() + "/" + name, ex); + } + } + + /** + * Detected mime type from name (file extension) using Mime Type service. + * Fallback to application/octet-stream. + * @param name Node name + * @return Mime type (never null) + */ + private String detectMimeTypeFromName(String name) { + String mimeType = null; + String fileExtension = StringUtils.substringAfterLast(name, "."); + if (bundleContext != null && StringUtils.isNotEmpty(fileExtension)) { + ServiceReference ref = bundleContext.getServiceReference(MimeTypeService.class.getName()); + if (ref != null) { + MimeTypeService mimeTypeService = (MimeTypeService) bundleContext.getService(ref); + mimeType = mimeTypeService.getMimeType(fileExtension); + } + } + return StringUtils.defaultString(mimeType, CONTENTTYPE_OCTET_STREAM); + } + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/ContentLoader.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/package-info.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/package-info.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/package-info.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/package-info.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,23 @@ +/* + * 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. + */ +/** + * Helpers for importing test content into the mocked repositories / resource hierarchies. + */ +package org.apache.sling.testing.mock.sling.loader; + Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/package-info.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/package-info.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/loader/package-info.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/package-info.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/package-info.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/package-info.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/package-info.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,23 @@ +/* + * 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. + */ +/** + * Mock implementation of selected Sling APIs. + */ +package org.apache.sling.testing.mock.sling; + Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/package-info.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/package-info.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/package-info.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockMimeTypeService.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockMimeTypeService.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockMimeTypeService.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockMimeTypeService.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,78 @@ +/* + * 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.testing.mock.sling.services; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.commons.mime.MimeTypeService; +import org.apache.sling.commons.mime.internal.MimeTypeServiceImpl; +import org.apache.sling.testing.mock.osgi.MockOsgi; +import org.osgi.service.component.ComponentContext; + +/** + * Mock {@link MimeTypeService} implementation. + */ +@Component(inherit = false) +@Service(MimeTypeService.class) +public final class MockMimeTypeService extends MimeTypeServiceImpl { + + private boolean initialized; + + /** + * Do lazy initializing because reading to avoid reading all defined mime + * types from disk if not required + */ + private void lazyInitialization() { + if (!this.initialized) { + this.initialized = true; + // activate service in simulated OSGi environment + ComponentContext componentContext = MockOsgi.newComponentContext(); + this.bindLogService(MockOsgi.newLogService(getClass())); + activate(componentContext); + } + } + + @Override + public String getMimeType(final String name) { + lazyInitialization(); + return super.getMimeType(name); + } + + @Override + public String getExtension(final String mimeType) { + lazyInitialization(); + return super.getExtension(mimeType); + } + + @Override + public void registerMimeType(final String mimeType, final String... extensions) { + lazyInitialization(); + super.registerMimeType(mimeType, extensions); + } + + @Override + public void registerMimeType(final InputStream mimeTabStream) throws IOException { + lazyInitialization(); + super.registerMimeType(mimeTabStream); + } + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockMimeTypeService.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockMimeTypeService.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockMimeTypeService.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockModelAdapterFactory.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockModelAdapterFactory.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockModelAdapterFactory.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockModelAdapterFactory.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,295 @@ +/* + * 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.testing.mock.sling.services; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.adapter.AdapterFactory; +import org.apache.sling.models.annotations.Model; +import org.apache.sling.models.impl.ModelAdapterFactory; +import org.apache.sling.models.spi.ImplementationPicker; +import org.apache.sling.models.spi.Injector; +import org.apache.sling.models.spi.injectorspecific.InjectAnnotationProcessorFactory; +import org.apache.sling.testing.mock.osgi.MockOsgi; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.Version; +import org.osgi.service.component.ComponentContext; +import org.reflections.Reflections; + +/** + * Mock {@link ModelAdapterFactory} implementation. + */ +@Component(inherit = false) +@Service(AdapterFactory.class) +public final class MockModelAdapterFactory extends ModelAdapterFactory { + + private final BundleContext bundleContext; + + /** + * @param componentContext OSGi component context + */ + public MockModelAdapterFactory(ComponentContext componentContext) { + bundleContext = componentContext.getBundleContext(); + + // register service listener to collect injectors + // this allows detecting injectors even if they are registered after + // this bundle + // (which is otherwise currently not supported in the osgi mock + // environment) + bundleContext.addServiceListener(new InjectorServiceListener()); + + // activate service in simulated OSGi environment + activate(componentContext); + } + + /** + * Constructor with default component context + */ + public MockModelAdapterFactory() { + this(MockOsgi.newComponentContext()); + } + + private class InjectorServiceListener implements ServiceListener { + + @Override + public void serviceChanged(ServiceEvent event) { + Object service = bundleContext.getService(event.getServiceReference()); + if (service instanceof Injector) { + if (event.getType() == ServiceEvent.REGISTERED) { + bindInjector((Injector) service, getServiceProperties(event.getServiceReference())); + } else if (event.getType() == ServiceEvent.UNREGISTERING) { + unbindInjector((Injector) service, getServiceProperties(event.getServiceReference())); + } + } + if (service instanceof InjectAnnotationProcessorFactory) { + if (event.getType() == ServiceEvent.REGISTERED) { + bindInjectAnnotationProcessorFactory((InjectAnnotationProcessorFactory) service, + getServiceProperties(event.getServiceReference())); + } else if (event.getType() == ServiceEvent.UNREGISTERING) { + unbindInjectAnnotationProcessorFactory((InjectAnnotationProcessorFactory) service, + getServiceProperties(event.getServiceReference())); + } + } + if (service instanceof ImplementationPicker) { + if (event.getType() == ServiceEvent.REGISTERED) { + bindImplementationPicker((ImplementationPicker) service, + getServiceProperties(event.getServiceReference())); + } else if (event.getType() == ServiceEvent.UNREGISTERING) { + unbindImplementationPicker((ImplementationPicker) service, + getServiceProperties(event.getServiceReference())); + } + } + } + + private Map<String, Object> getServiceProperties(ServiceReference reference) { + Map<String, Object> props = new HashMap<String, Object>(); + String[] propertyKeys = reference.getPropertyKeys(); + for (String key : propertyKeys) { + props.put(key, reference.getProperty(key)); + } + return props; + } + + } + + /** + * Scan classpaths for given package name (and sub packages) to scan for and + * register all classes with @Model annotation. + * @param packageName Java package name + */ + public void addModelsForPackage(String packageName) { + Bundle bundle = new ModelsPackageBundle(packageName, Bundle.ACTIVE); + BundleEvent event = new BundleEvent(BundleEvent.STARTED, bundle); + MockOsgi.sendBundleEvent(this.bundleContext, event); + } + + @SuppressWarnings("unused") + private class ModelsPackageBundle implements Bundle { + + private final String packageName; + private final int state; + + public ModelsPackageBundle(String packageName, int state) { + this.packageName = packageName; + this.state = state; + } + + @Override + public int getState() { + return this.state; + } + + @Override + public Dictionary getHeaders() { + Dictionary<String, Object> headers = new Hashtable<String, Object>(); + headers.put("Sling-Model-Packages", this.packageName); + return headers; + } + + @Override + public Enumeration findEntries(String path, String filePattern, boolean recurse) { + Reflections reflections = new Reflections(this.packageName); + Set<Class<?>> types = reflections.getTypesAnnotatedWith(Model.class); + Vector<URL> urls = new Vector<URL>(); // NOPMD + try { + for (Class<?> type : types) { + urls.add(new URL("file:/" + type.getName().replace('.', '/') + ".class")); + } + } catch (MalformedURLException ex) { + throw new RuntimeException("Malformed URL.", ex); + } + return urls.elements(); + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return getClass().getClassLoader().loadClass(name); + } + + @Override + public BundleContext getBundleContext() { + return bundleContext; + } + + @Override + public void start(int options) throws BundleException { + // do nothing + } + + @Override + public void start() throws BundleException { + // do nothing + } + + @Override + public void stop(int options) throws BundleException { + // do nothing + } + + @Override + public void stop() throws BundleException { + // do nothing + } + + @Override + public void update(InputStream input) throws BundleException { + // do nothing + } + + @Override + public void update() throws BundleException { + // do nothing + } + + @Override + public void uninstall() throws BundleException { + // do nothing + } + + @Override + public long getBundleId() { + return 0; + } + + @Override + public String getLocation() { + return null; + } + + @Override + public ServiceReference[] getRegisteredServices() { // NOPMD + return null; + } + + @Override + public ServiceReference[] getServicesInUse() { // NOPMD + return null; + } + + @Override + public boolean hasPermission(Object permission) { + return false; + } + + @Override + public URL getResource(String name) { + return null; + } + + @Override + public Dictionary getHeaders(String locale) { + return null; + } + + @Override + public String getSymbolicName() { + return null; + } + + @Override + public Enumeration getResources(String name) throws IOException { + return null; + } + + @Override + public Enumeration getEntryPaths(String path) { + return null; + } + + @Override + public URL getEntry(String path) { + return null; + } + + @Override + public long getLastModified() { + return 0; + } + + // this is part of org.osgi 4.2.0 + public Map getSignerCertificates(int signersType) { + return null; + } + + // this is part of org.osgi 4.2.0 + public Version getVersion() { + return null; + } + + } + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockModelAdapterFactory.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockModelAdapterFactory.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockModelAdapterFactory.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,90 @@ +/* + * 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.testing.mock.sling.services; + +import java.net.URL; +import java.util.Set; + +import org.apache.sling.settings.SlingSettingsService; + +import com.google.common.collect.ImmutableSet; + +/** + * Mock implementation of {@link SlingSettingsService}. + */ +public final class MockSlingSettingService implements SlingSettingsService { + + private Set<String> runModes; + + /** + * Instantiate with no default run modes. + */ + public MockSlingSettingService() { + this(ImmutableSet.<String> of()); + } + + /** + * Instantiate with given run modes + * @param defaultRunModes Run modes + */ + public MockSlingSettingService(Set<String> defaultRunModes) { + this.runModes = defaultRunModes; + } + + @Override + public Set<String> getRunModes() { + return ImmutableSet.copyOf(this.runModes); + } + + public void setRunModes(Set<String> runModes) { + this.runModes = runModes; + } + + // --- unsupported operations --- + @Override + public String getAbsolutePathWithinSlingHome(String relativePath) { + throw new UnsupportedOperationException(); + } + + @Override + public String getSlingId() { + throw new UnsupportedOperationException(); + } + + @Override + public String getSlingHomePath() { + throw new UnsupportedOperationException(); + } + + @Override + public URL getSlingHome() { + throw new UnsupportedOperationException(); + } + + // part of Sling API 2.7 + public String getSlingName() { + throw new UnsupportedOperationException(); + } + + // part of Sling API 2.7 + public String getSlingDescription() { + throw new UnsupportedOperationException(); + } + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/services/MockSlingSettingService.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/CookieSupport.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/CookieSupport.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/CookieSupport.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/CookieSupport.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,53 @@ +/* + * 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.testing.mock.sling.servlet; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.servlet.http.Cookie; + +/** + * Manages cookies for request and response. + */ +class CookieSupport { + + private Map<String, Cookie> cookies = new LinkedHashMap<String, Cookie>(); + + public void addCookie(Cookie cookie) { + cookies.put(cookie.getName(), cookie); + } + + public Cookie getCookie(String name) { + return cookies.get(name); + } + + public Cookie[] getCookies() { + if (cookies.isEmpty()) { + return null; + } else { + return cookies.values().toArray(new Cookie[cookies.size()]); + } + } + + public void reset() { + cookies.clear(); + } + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/CookieSupport.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/CookieSupport.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/CookieSupport.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/HeaderSupport.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/HeaderSupport.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/HeaderSupport.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/HeaderSupport.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,175 @@ +/* + * 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.testing.mock.sling.servlet; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.TimeZone; +import java.util.Vector; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; + +/** + * Manage HTTP headers for request and response. + */ +class HeaderSupport { + + private static final String RFC_1123_DATE_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z"; + private static final DateFormat RFC1123_DATE_FORMAT = new SimpleDateFormat(RFC_1123_DATE_PATTERN, Locale.US); + private static final TimeZone TIMEZONE_GMT = TimeZone.getTimeZone("GMT"); + static { + RFC1123_DATE_FORMAT.setTimeZone(TIMEZONE_GMT); + } + + private List<HeaderValue> headers = new ArrayList<HeaderValue>(); + + private static class HeaderValue { + + private String key; + private String value; + + public HeaderValue(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return this.key; + } + + public String getValue() { + return this.value; + } + + } + + public void addHeader(String name, String value) { + headers.add(new HeaderValue(name, value)); + } + + public void addIntHeader(String name, int value) { + headers.add(new HeaderValue(name, Integer.toString(value))); + } + + public void addDateHeader(String name, long date) { + Calendar calendar = Calendar.getInstance(TIMEZONE_GMT, Locale.US); + calendar.setTimeInMillis(date); + headers.add(new HeaderValue(name, formatDate(calendar))); + } + + public void setHeader(String name, String value) { + removeHeaders(name); + addHeader(name, value); + } + + public void setIntHeader(String name, int value) { + removeHeaders(name); + addIntHeader(name, value); + } + + public void setDateHeader(String name, long date) { + removeHeaders(name); + addDateHeader(name, date); + } + + private void removeHeaders(String name) { + for (int i = this.headers.size() - 1; i >= 0; i--) { + if (StringUtils.equals(this.headers.get(i).getKey(), name)) { + headers.remove(i); + } + } + } + + public boolean containsHeader(String name) { + return !getHeaders(name).isEmpty(); + } + + public String getHeader(String name) { + Collection<String> values = getHeaders(name); + if (!values.isEmpty()) { + return values.iterator().next(); + } else { + return null; + } + } + + public int getIntHeader(String name) { + String value = getHeader(name); + return NumberUtils.toInt(value); + } + + public long getDateHeader(String name) { + String value = getHeader(name); + if (StringUtils.isEmpty(value)) { + return 0L; + } else { + try { + return parseDate(value).getTimeInMillis(); + } catch (ParseException ex) { + return 0L; + } + } + } + + public Collection<String> getHeaders(String name) { + List<String> values = new ArrayList<String>(); + for (HeaderValue entry : headers) { + if (StringUtils.equals(entry.getKey(), name)) { + values.add(entry.getValue()); + } + } + return values; + } + + public Collection<String> getHeaderNames() { + Set<String> values = new HashSet<String>(); + for (HeaderValue entry : headers) { + values.add(entry.getKey()); + } + return values; + } + + public void reset() { + headers.clear(); + } + + public static Enumeration<String> toEnumeration(Collection<String> collection) { + return new Vector<String>(collection).elements(); + } + + private static synchronized String formatDate(Calendar date) { + return RFC1123_DATE_FORMAT.format(date.getTime()); + } + + private static synchronized Calendar parseDate(String dateString) throws ParseException { + RFC1123_DATE_FORMAT.parse(dateString); + return RFC1123_DATE_FORMAT.getCalendar(); + } + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/HeaderSupport.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/HeaderSupport.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/HeaderSupport.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockHttpSession.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockHttpSession.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockHttpSession.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockHttpSession.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,129 @@ +/* + * 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.testing.mock.sling.servlet; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpSession; + +import org.apache.commons.collections.IteratorUtils; + +/** + * Mock {@link HttpSession} implementation. + */ +public final class MockHttpSession implements HttpSession { + + private final ServletContext servletContext = new MockServletContext(); + private final Map<String, Object> attributeMap = new HashMap<String, Object>(); + private final String sessionID = UUID.randomUUID().toString(); + private final long creationTime = System.currentTimeMillis(); + + @Override + public ServletContext getServletContext() { + return this.servletContext; + } + + @Override + public Object getAttribute(final String name) { + return this.attributeMap.get(name); + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration<String> getAttributeNames() { + return IteratorUtils.asEnumeration(this.attributeMap.keySet().iterator()); + } + + @Override + public String getId() { + return this.sessionID; + } + + @Override + public long getCreationTime() { + return this.creationTime; + } + + @Override + public Object getValue(final String name) { + return getAttribute(name); + } + + @Override + public String[] getValueNames() { + return this.attributeMap.keySet().toArray(new String[this.attributeMap.keySet().size()]); + } + + @Override + public void putValue(final String name, final Object value) { + setAttribute(name, value); + } + + @Override + public void removeAttribute(final String name) { + this.attributeMap.remove(name); + } + + @Override + public void removeValue(final String name) { + this.attributeMap.remove(name); + } + + @Override + public void setAttribute(final String name, final Object value) { + this.attributeMap.put(name, value); + } + + // --- unsupported operations --- + @Override + public long getLastAccessedTime() { + throw new UnsupportedOperationException(); + } + + @Override + public int getMaxInactiveInterval() { + throw new UnsupportedOperationException(); + } + + @Override + @SuppressWarnings("deprecation") + public javax.servlet.http.HttpSessionContext getSessionContext() { + throw new UnsupportedOperationException(); + } + + @Override + public void invalidate() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isNew() { + throw new UnsupportedOperationException(); + } + + @Override + public void setMaxInactiveInterval(final int interval) { + throw new UnsupportedOperationException(); + } + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockHttpSession.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockHttpSession.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockHttpSession.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockRequestPathInfo.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockRequestPathInfo.java?rev=1631356&view=auto ============================================================================== --- sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockRequestPathInfo.java (added) +++ sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockRequestPathInfo.java Mon Oct 13 11:54:39 2014 @@ -0,0 +1,86 @@ +/* + * 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.testing.mock.sling.servlet; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.request.RequestPathInfo; +import org.apache.sling.api.resource.Resource; + +/** + * Mock {@link RequestPathInfo} implementation. + */ +public final class MockRequestPathInfo implements RequestPathInfo { + + private String extension; + private String resourcePath; + private String selectorString; + private String suffix; + + @Override + public String getExtension() { + return this.extension; + } + + @Override + public String getResourcePath() { + return this.resourcePath; + } + + @Override + public String[] getSelectors() { + if (StringUtils.isEmpty(this.selectorString)) { + return new String[0]; + } else { + return StringUtils.split(this.selectorString, "."); + } + } + + @Override + public String getSelectorString() { + return this.selectorString; + } + + @Override + public String getSuffix() { + return this.suffix; + } + + public void setExtension(final String extension) { + this.extension = extension; + } + + public void setResourcePath(final String resourcePath) { + this.resourcePath = resourcePath; + } + + public void setSelectorString(final String selectorString) { + this.selectorString = selectorString; + } + + public void setSuffix(final String suffix) { + this.suffix = suffix; + } + + // --- unsupported operations --- + @Override + public Resource getSuffixResource() { + throw new UnsupportedOperationException(); + } + +} Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockRequestPathInfo.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockRequestPathInfo.java ------------------------------------------------------------------------------ --- svn:keywords (added) +++ svn:keywords Mon Oct 13 11:54:39 2014 @@ -0,0 +1 @@ +LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author Propchange: sling/trunk/testing/sling-mock/src/main/java/org/apache/sling/testing/mock/sling/servlet/MockRequestPathInfo.java ------------------------------------------------------------------------------ svn:mime-type = text/plain