blob: 2c2aeb66ed2a28990b729650fe5cea7eeea439f6 [file] [log] [blame]
/*
* 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.solr.rest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.SuppressForbidden;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.rest.ManagedResourceStorage.StorageIO;
import org.junit.Test;
/**
* Tests {@link ManagedResource} functionality.
*/
public class TestManagedResource extends SolrTestCaseJ4 {
/**
* Mock class that acts like an analysis component that depends on
* data managed by a ManagedResource
*/
private static class MockAnalysisComponent implements ManagedResourceObserver {
private boolean wasNotified = false;
@SuppressWarnings("unchecked")
@Override
public void onManagedResourceInitialized(NamedList<?> args, ManagedResource res) throws SolrException {
assertEquals("someVal", args.get("someArg"));
assertTrue(res instanceof ManagedTestResource);
ManagedTestResource testRes = (ManagedTestResource)res;
List<String> data = (List<String>)testRes.managedData;
assertTrue(data.contains("1"));
assertTrue(data.contains("2"));
assertTrue(data.contains("3"));
wasNotified = true;
}
}
private class ManagedTestResource extends ManagedResource {
private Object managedData;
private ManagedTestResource(String resourceId, SolrResourceLoader loader,
StorageIO storageIO) throws SolrException {
super(resourceId, loader, storageIO);
}
@Override
protected void onManagedDataLoadedFromStorage(NamedList<?> managedInitArgs, Object managedData)
throws SolrException {
assertNotNull(managedData);
assertTrue(managedData instanceof List);
// {'initArgs':{'someArg':'someVal', 'arg2':true, 'arg3':['one','two','three'],
// 'arg4':18, 'arg5':0.9, 'arg6':{ 'uno':1, 'dos':2 }},'"
assertEquals("someVal", managedInitArgs.get("someArg"));
assertEquals(true, managedInitArgs.get("arg2"));
List<String> arg3List = Arrays.asList("one", "two", "three");
assertEquals(arg3List, managedInitArgs.get("arg3"));
assertEquals(18L, managedInitArgs.get("arg4"));
assertEquals(0.9, managedInitArgs.get("arg5"));
Map<String,Long> arg6map = new LinkedHashMap<>(2);
arg6map.put("uno", 1L);
arg6map.put("dos", 2L);
assertEquals(arg6map, managedInitArgs.get("arg6"));
this.managedData = managedData;
}
// NOTE: These methods are better tested from the REST API
// so they are stubbed out here and not used in this test
@Override
protected Object applyUpdatesToManagedData(Object updates) {
return null;
}
@Override
public void doDeleteChild(BaseSolrResource endpoint, String childId) {}
@Override
public void doGet(BaseSolrResource endpoint, String childId) {}
}
/**
* Implements a Java serialization based storage format.
*/
@SuppressForbidden(reason = "XXX: security hole")
private static class SerializableStorage extends ManagedResourceStorage {
SerializableStorage(StorageIO storageIO, SolrResourceLoader loader) {
super(storageIO, loader);
}
@SuppressForbidden(reason = "XXX: security hole")
@Override
public Object load(String resourceId) throws IOException {
String storedId = getStoredResourceId(resourceId);
InputStream inputStream = storageIO.openInputStream(storedId);
if (inputStream == null) {
return null;
}
Object serialized = null;
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(inputStream);
serialized = ois.readObject();
} catch (ClassNotFoundException e) {
// unlikely
throw new IOException(e);
} finally {
if (ois != null) {
try {
ois.close();
} catch (Exception ignore){}
}
}
return serialized;
}
@SuppressForbidden(reason = "XXX: security hole")
@Override
public void store(String resourceId, Object toStore) throws IOException {
if (!(toStore instanceof Serializable))
throw new IOException("Instance of "+
toStore.getClass().getName()+" is not Serializable!");
String storedId = getStoredResourceId(resourceId);
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(storageIO.openOutputStream(storedId));
oos.writeObject(toStore);
oos.flush();
} finally {
if (oos != null) {
try {
oos.close();
} catch (Exception ignore){}
}
}
}
@Override
public String getStoredResourceId(String resourceId) {
return resourceId.replace('/','_')+".bin";
}
}
private class CustomStorageFormatResource extends ManagedTestResource {
private CustomStorageFormatResource(String resourceId, SolrResourceLoader loader,
StorageIO storageIO) throws SolrException {
super(resourceId, loader, storageIO);
}
@Override
protected ManagedResourceStorage createStorage(StorageIO storageIO, SolrResourceLoader loader)
throws SolrException
{
return new SerializableStorage(storageIO, loader);
}
}
/**
* Tests managed data storage to and loading from {@link ManagedResourceStorage.InMemoryStorageIO}.
*/
@SuppressWarnings("unchecked")
@Test
public void testLoadingAndStoringOfManagedData() throws Exception {
String resourceId = "/config/test/foo";
String storedResourceId = "_config_test_foo.json";
MockAnalysisComponent observer = new MockAnalysisComponent();
List<ManagedResourceObserver> observers =
Arrays.asList((ManagedResourceObserver)observer);
// put some data in the storage impl so that we can test
// initialization of managed data from storage
String storedJson = "{'initArgs':{'someArg':'someVal', 'arg2':true, 'arg3':['one','two','three'],"
+ " 'arg4':18, 'arg5':0.9, 'arg6':{ 'uno':1, 'dos':2}},'"
+ ManagedResource.MANAGED_JSON_LIST_FIELD+"':['1','2','3']}";
ManagedResourceStorage.InMemoryStorageIO storageIO =
new ManagedResourceStorage.InMemoryStorageIO();
storageIO.storage.put(storedResourceId, new BytesRef(json(storedJson)));
ManagedTestResource res =
new ManagedTestResource(resourceId, new SolrResourceLoader(Paths.get("./")), storageIO);
res.loadManagedDataAndNotify(observers);
assertTrue("Observer was not notified by ManagedResource!", observer.wasNotified);
// now update the managed data (as if it came from the REST API)
List<String> updatedData = new ArrayList<>();
updatedData.add("1");
updatedData.add("2");
updatedData.add("3");
updatedData.add("4");
res.storeManagedData(updatedData);
Map<String,Object> jsonObject =
(Map<String,Object>) Utils.fromJSONString(storageIO.storage.get(storedResourceId).utf8ToString());
List<String> jsonList =
(List<String>)jsonObject.get(ManagedResource.MANAGED_JSON_LIST_FIELD);
assertTrue("Managed data was not updated correctly!", jsonList.contains("4"));
}
/**
* The ManagedResource storage framework allows the end developer to use a different
* storage format other than JSON, as demonstrated by this test.
*/
@SuppressWarnings("rawtypes")
@Test
public void testCustomStorageFormat() throws Exception {
String resourceId = "/schema/test/foo";
String storedResourceId = "_schema_test_foo.bin";
MockAnalysisComponent observer = new MockAnalysisComponent();
List<ManagedResourceObserver> observers =
Arrays.asList((ManagedResourceObserver)observer);
// put some data in the storage impl so that we can test
// initialization of managed data from storage
Map<String,Object> storedData = new HashMap<>();
Map<String,Object> initArgs = new HashMap<>();
// {'initArgs':{'someArg':'someVal', 'arg2':true, 'arg3':['one','two','three'],
// 'arg4':18, 'arg5':0.9, 'arg6':{ 'uno':1, 'dos':2 }},'"
initArgs.put("someArg", "someVal");
initArgs.put("arg2", Boolean.TRUE);
List<String> arg3list = Arrays.asList("one", "two", "three");
initArgs.put("arg3", arg3list);
initArgs.put("arg4", 18L);
initArgs.put("arg5", 0.9);
Map<String,Long> arg6map = new HashMap<>();
arg6map.put("uno", 1L);
arg6map.put("dos", 2L);
initArgs.put("arg6", arg6map);
storedData.put("initArgs", initArgs);
List<String> managedList = new ArrayList<>();
managedList.add("1");
managedList.add("2");
managedList.add("3");
storedData.put(ManagedResource.MANAGED_JSON_LIST_FIELD, managedList);
ManagedResourceStorage.InMemoryStorageIO storageIO =
new ManagedResourceStorage.InMemoryStorageIO();
storageIO.storage.put(storedResourceId, ser2bytes((Serializable)storedData));
CustomStorageFormatResource res =
new CustomStorageFormatResource(resourceId, new SolrResourceLoader(Paths.get("./")), storageIO);
res.loadManagedDataAndNotify(observers);
assertTrue("Observer was not notified by ManagedResource!", observer.wasNotified);
// now store some data (as if it came from the REST API)
List<String> updatedData = new ArrayList<>();
updatedData.add("1");
updatedData.add("2");
updatedData.add("3");
updatedData.add("4");
res.storeManagedData(updatedData);
Object stored = res.storage.load(resourceId);
assertNotNull(stored);
assertTrue(stored instanceof Map);
Map storedMap = (Map)stored;
assertNotNull(storedMap.get("initArgs"));
List storedList = (List)storedMap.get(ManagedResource.MANAGED_JSON_LIST_FIELD);
assertTrue(storedList.contains("4"));
}
/**
* Converts the given Serializable object to bytes
*/
@SuppressForbidden(reason = "XXX: security hole")
private BytesRef ser2bytes(Serializable ser) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(out);
oos.writeObject(ser);
oos.flush();
} finally {
if (oos != null) {
try {
oos.close();
} catch (Exception ignore){}
}
}
return new BytesRef(out.toByteArray());
}
}