/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community 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://opensource.org/licenses/ecl2.txt
*
* 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.opencastproject.ingest.endpoint;
import org.opencastproject.ingest.api.IngestService;
import org.opencastproject.mediapackage.MediaPackage;
import org.opencastproject.mediapackage.MediaPackageBuilderFactory;
import org.opencastproject.mediapackage.MediaPackageElementFlavor;
import org.opencastproject.mediapackage.MediaPackageParser;
import org.opencastproject.mediapackage.identifier.UUIDIdBuilderImpl;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.UploadJob;
import org.opencastproject.workflow.api.WorkflowInstance;
import org.opencastproject.workflow.api.WorkflowInstanceImpl;
import org.apache.commons.fileupload.MockHttpServletRequest;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Map;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
public class IngestRestServiceTest {
private static final Logger logger = LoggerFactory.getLogger(IngestRestServiceTest.class);
protected IngestRestService restService;
private File testDir = null;
private LimitVerifier limitVerifier;
@Before
public void setUp() throws Exception {
testDir = new File("./target", "ingest-rest-service-test");
if (testDir.exists()) {
FileUtils.deleteQuietly(testDir);
logger.info("Removing " + testDir.getAbsolutePath());
} else {
logger.info("Didn't Delete " + testDir.getAbsolutePath());
}
testDir.mkdir();
restService = new IngestRestService();
// Create a mock ingest service
IngestService ingestService = EasyMock.createNiceMock(IngestService.class);
EasyMock.expect(ingestService.createMediaPackage()).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(ingestService.createMediaPackage("1a6f70ab-4262-4523-9f8e-babce22a1ea8")).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew(new UUIDIdBuilderImpl().fromString("1a6f70ab-4262-4523-9f8e-babce22a1ea8")));
EasyMock.expect(
ingestService.addAttachment((URI) EasyMock.anyObject(), (MediaPackageElementFlavor) EasyMock.anyObject(),
(MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addCatalog((URI) EasyMock.anyObject(), (MediaPackageElementFlavor) EasyMock.anyObject(),
(MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addTrack((URI) EasyMock.anyObject(), (MediaPackageElementFlavor) EasyMock.anyObject(),
(MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addTrack((URI) EasyMock.anyObject(), (MediaPackageElementFlavor) EasyMock.anyObject(),
(String[]) EasyMock.anyObject(), (MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addAttachment((InputStream) EasyMock.anyObject(), (String) EasyMock.anyObject(),
(MediaPackageElementFlavor) EasyMock.anyObject(), (MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addAttachment((InputStream) EasyMock.anyObject(), (String) EasyMock.anyObject(),
(MediaPackageElementFlavor) EasyMock.anyObject(), (String[]) EasyMock.anyObject(), (MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addCatalog((InputStream) EasyMock.anyObject(), (String) EasyMock.anyObject(),
(MediaPackageElementFlavor) EasyMock.anyObject(), (MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addCatalog((InputStream) EasyMock.anyObject(), (String) EasyMock.anyObject(),
(MediaPackageElementFlavor) EasyMock.anyObject(), (String[]) EasyMock.anyObject(), (MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addTrack((InputStream) EasyMock.anyObject(), (String) EasyMock.anyObject(),
(MediaPackageElementFlavor) EasyMock.anyObject(), (MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addTrack((InputStream) EasyMock.anyObject(), (String) EasyMock.anyObject(),
(MediaPackageElementFlavor) EasyMock.anyObject(), (String[]) EasyMock.anyObject(), (MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.expect(
ingestService.addPartialTrack((InputStream) EasyMock.anyObject(), (String) EasyMock.anyObject(),
(MediaPackageElementFlavor) EasyMock.anyObject(), EasyMock.anyLong(),
(MediaPackage) EasyMock.anyObject())).andReturn(
MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew());
EasyMock.replay(ingestService);
// Set the service, and activate the rest endpoint
restService.setIngestService(ingestService);
restService.activate(null);
}
@Test
public void testNoIngestLimit() {
setupAndTestLimit(null, -1, false);
}
@Test
public void testIngestLimitOfNegativeOne() {
setupAndTestLimit("-1", -1, false);
}
@Test
public void testIngestLimitOfZero() {
setupAndTestLimit("0", -1, false);
}
@Test
public void testIngestLimitOfOne() {
setupAndTestLimit("1", 1, true);
}
@Test
public void testIngestLimitOfTen() {
setupAndTestLimit("10", 10, true);
}
@Test
public void testIngestLimitOfThousand() {
setupAndTestLimit("1000", 1000, true);
}
@Test
public void testInvalidLimitAddZippedMediaPackage() {
setupAndTestLimit("This is not a number", -1, false);
}
public void setupAndTestLimit(String limit, int expectedLimit, boolean expectedEnabled) {
restService = new IngestRestService();
// Create a mock ingest service
IngestService ingestService = EasyMock.createNiceMock(IngestService.class);
ComponentContext cc = EasyMock.createNiceMock(ComponentContext.class);
BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
EasyMock.expect(cc.getBundleContext()).andReturn(bc).anyTimes();
EasyMock.replay(cc);
EasyMock.expect(bc.getProperty(IngestRestService.DEFAULT_WORKFLOW_DEFINITION)).andReturn("full").anyTimes();
if (StringUtils.trimToNull(limit) != null) {
EasyMock.expect(bc.getProperty(IngestRestService.MAX_INGESTS_KEY)).andReturn(limit).anyTimes();
}
EasyMock.replay(bc);
restService.setIngestService(ingestService);
restService.activate(cc);
Assert.assertEquals(expectedLimit, restService.getIngestLimit());
Assert.assertEquals(expectedEnabled, restService.isIngestLimitEnabled());
}
@Test
public void testLimitOfOneToAddZippedMediaPackage() {
setupAndTestIngestingLimit("1", 1, 1, 0);
}
@Test
public void testLimitOfOneWithTwoIngestsToAddZippedMediaPackage() {
setupAndTestIngestingLimit("1", 2, 1, 1);
}
@Test
public void testLimitOfOneWithTenIngestsToAddZippedMediaPackage() {
setupAndTestIngestingLimit("1", 10, 1, 9);
}
@Test
public void testLimitOfTwoWithTenIngestsToAddZippedMediaPackage() {
setupAndTestIngestingLimit("2", 10, 2, 8);
}
@Test
public void testLimitOfFiveWithTenIngestsToAddZippedMediaPackage() {
setupAndTestIngestingLimit("5", 10, 5, 5);
}
@Test
public void testLimitOfTenWithOneHundredIngestsToAddZippedMediaPackage() {
setupAndTestIngestingLimit("10", 100, 10, 90);
}
@Test
public void testLimitOfZeroWithTenIngestsToAddZippedMediaPackage() {
setupAndTestIngestingLimit("0", 10, 10, 0);
}
public void setupAndTestIngestingLimit(String limit, int numberOfIngests, int expectedOK, int expectedBusy) {
restService = new IngestRestService();
restService.setIngestService(setupAddZippedMediaPackageIngestService());
restService.activate(setupAddZippedMediaPackageComponentContext(limit));
limitVerifier = new LimitVerifier(numberOfIngests);
limitVerifier.start();
Assert.assertEquals("There should be no errors when making requests.", 0, limitVerifier.getError());
Assert.assertEquals("There should have been the same number of successful ingests finished as expected.",
expectedOK, limitVerifier.getOk());
Assert.assertEquals("The extra ingests beyond the limit should have received a server unavailable error. ",
expectedBusy, limitVerifier.getUnavailable());
}
private class LimitVerifier {
private int numberOfIngests;
private int current;
private int unavailable = 0;
private int ok = 0;
private int error = 0;
LimitVerifier(int numberOfIngests) {
this.numberOfIngests = numberOfIngests;
}
public void start() {
getResponse();
}
private void getResponse() {
current++;
if (current > numberOfIngests) {
return;
} else {
Response response = restService.addZippedMediaPackage(setupAddZippedMediaPackageHttpServletRequest());
if (response.getStatus() == Status.SERVICE_UNAVAILABLE.getStatusCode()) {
unavailable++;
// Because there is no mock that gets called if the service is unavailable we will have to do the next request
// here.
getResponse();
} else if (response.getStatus() == Status.OK.getStatusCode()) {
ok++;
} else {
error++;
}
}
}
public void callback() {
getResponse();
}
private synchronized int getUnavailable() {
return unavailable;
}
private synchronized int getOk() {
return ok;
}
private synchronized int getError() {
return error;
}
}
private ServletInputStream setupAddZippedMediaPackageServletInputStream() {
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public int read() throws IOException {
return 0;
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
return servletInputStream;
}
private HttpServletRequest setupAddZippedMediaPackageHttpServletRequest() {
HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
EasyMock.expect(request.getMethod()).andReturn("post");
try {
EasyMock.expect(request.getInputStream()).andReturn(setupAddZippedMediaPackageServletInputStream());
} catch (IOException e) {
Assert.fail("Failed due to exception " + e.getMessage());
}
EasyMock.replay(request);
return request;
}
private BundleContext setupAddZippedMediaPackageBundleContext(String limit) {
BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
EasyMock.expect(bc.getProperty(IngestRestService.DEFAULT_WORKFLOW_DEFINITION)).andReturn("full").anyTimes();
if (StringUtils.trimToNull(limit) != null) {
EasyMock.expect(bc.getProperty(IngestRestService.MAX_INGESTS_KEY)).andReturn(limit).anyTimes();
}
EasyMock.replay(bc);
return bc;
}
private ComponentContext setupAddZippedMediaPackageComponentContext(String limit) {
ComponentContext cc = EasyMock.createNiceMock(ComponentContext.class);
EasyMock.expect(cc.getBundleContext()).andReturn(setupAddZippedMediaPackageBundleContext(limit)).anyTimes();
EasyMock.replay(cc);
return cc;
}
@SuppressWarnings("unchecked")
private IngestService setupAddZippedMediaPackageIngestService() {
// Create a mock ingest service
IngestService ingestService = EasyMock.createNiceMock(IngestService.class);
try {
EasyMock.expect(
ingestService.addZippedMediaPackage((InputStream) EasyMock.anyObject(), (String) EasyMock.anyObject(),
(Map<String, String>) EasyMock.anyObject(), EasyMock.anyLong()))
.andAnswer(new IAnswer<WorkflowInstance>() {
@Override
public WorkflowInstance answer() throws Throwable {
limitVerifier.callback();
return new WorkflowInstanceImpl();
}
}).anyTimes();
} catch (Exception e) {
Assert.fail("Threw exception " + e.getMessage());
}
EasyMock.replay(ingestService);
return ingestService;
}
@Test
public void testCreateMediaPackage() throws Exception {
Response response = restService.createMediaPackage();
Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus());
MediaPackage mp = (MediaPackage) response.getEntity();
Assert.assertNotNull(mp);
response = restService.createMediaPackage("1a6f70ab-4262-4523-9f8e-babce22a1ea8");
Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus());
mp = (MediaPackage) response.getEntity();
Assert.assertNotNull(mp);
}
@Test
public void testAddMediaPackageTrack() throws Exception {
Response response = restService.addMediaPackageTrack("http://foo/av.mov", "presenter/source","testtag",
MediaPackageParser.getAsXml(((MediaPackage) restService.createMediaPackage().getEntity())));
Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
@Test
public void testAddMediaPackageCatalog() throws Exception {
Response response = restService.addMediaPackageCatalog("http://foo/dc.xml", "dublincore/episode",
MediaPackageParser.getAsXml(((MediaPackage) restService.createMediaPackage().getEntity())));
Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
@Test
public void testAddMediaPackageAttachment() throws Exception {
Response response = restService.addMediaPackageAttachment("http://foo/cover.png", "image/cover",
MediaPackageParser.getAsXml(((MediaPackage) restService.createMediaPackage().getEntity())));
Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
@Test
public void testAddMediaPackageAttachmentFromRequest() throws Exception {
// Upload the mediapackage with its new element
Response postResponse = restService.addMediaPackageAttachment(newMockRequest());
Assert.assertEquals(Status.OK.getStatusCode(), postResponse.getStatus());
}
@Test
public void testAddMediaPackageCatalogFromRequest() throws Exception {
// Upload the mediapackage with its new element
Response postResponse = restService.addMediaPackageCatalog(newMockRequest());
Assert.assertEquals(Status.OK.getStatusCode(), postResponse.getStatus());
}
@Test
public void testAddMediaPackageTrackFromRequest() throws Exception {
// Upload the mediapackage with its new element
Response postResponse = restService.addMediaPackageTrack(newMockRequest());
Assert.assertEquals(Status.OK.getStatusCode(), postResponse.getStatus());
}
@Test
public void testUploadJobPersistence() throws Exception {
// Create the job (this is done in a browser on the initial page load)
UploadJob job = restService.createUploadJob();
// Upload the mediapackage with its new element
Response postResponse = restService.addElementMonitored(job.getId(), newMockRequest());
Assert.assertEquals(Status.OK.getStatusCode(), postResponse.getStatus());
// Check on the job status
Response progressResponse = restService.getProgress(job.getId());
Assert.assertEquals(Status.OK.getStatusCode(), progressResponse.getStatus());
Assert.assertTrue(progressResponse.getEntity().toString().startsWith("{\"total\":"));
// Check on a job that doesn't exist
try {
restService.getProgress("This ID does not exist");
Assert.fail("The rest service should throw");
} catch (NotFoundException e) {
// expected
}
}
@Test
public void testAddMediaPackagePartialTrack() throws Exception {
String mediaPackage = MediaPackageParser.getAsXml(((MediaPackage) restService.createMediaPackage().getEntity()));
Response response = restService.addMediaPackagePartialTrack("http://foo/av.mov", "presenter/source+partial", 1000L,
mediaPackage);
Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus());
response = restService.addMediaPackagePartialTrack(newPartialMockRequest());
Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
private HttpServletRequest newMockRequest() throws Exception {
MediaPackage mp = MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew();
StringBuilder requestBody = new StringBuilder();
requestBody.append("-----1234\r\n");
requestBody.append("Content-Disposition: form-data; name=\"flavor\"\r\n");
requestBody.append("\r\ntest/flavor\r\n");
requestBody.append("-----1234\r\n");
requestBody.append("Content-Disposition: form-data; name=\"mediaPackage\"\r\n");
requestBody.append("\r\n");
requestBody.append(MediaPackageParser.getAsXml(mp));
requestBody.append("\r\n");
requestBody.append("-----1234\r\n");
requestBody.append("Content-Disposition: form-data; name=\"file\"; filename=\"catalog.txt\"\r\n");
requestBody.append("Content-Type: text/whatever\r\n");
requestBody.append("\r\n");
requestBody.append("This is the content of the file\n");
requestBody.append("\r\n");
requestBody.append("-----1234");
return new MockHttpServletRequest(requestBody.toString().getBytes("UTF-8"), "multipart/form-data; boundary=---1234");
}
private HttpServletRequest newPartialMockRequest() throws Exception {
MediaPackage mp = MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().createNew();
StringBuilder requestBody = new StringBuilder();
requestBody.append("-----1234\r\n");
requestBody.append("Content-Disposition: form-data; name=\"flavor\"\r\n");
requestBody.append("\r\ntest/flavor\r\n");
requestBody.append("-----1234\r\n");
requestBody.append("Content-Disposition: form-data; name=\"mediaPackage\"\r\n");
requestBody.append("\r\n");
requestBody.append(MediaPackageParser.getAsXml(mp));
requestBody.append("\r\n");
requestBody.append("-----1234\r\n");
requestBody.append("Content-Disposition: form-data; name=\"startTime\"\r\n");
requestBody.append("\r\n2000\r\n");
requestBody.append("-----1234\r\n");
requestBody.append("Content-Disposition: form-data; name=\"file\"; filename=\"catalog.txt\"\r\n");
requestBody.append("Content-Type: text/whatever\r\n");
requestBody.append("\r\n");
requestBody.append("This is the content of the file\n");
requestBody.append("\r\n");
requestBody.append("-----1234");
return new MockHttpServletRequest(requestBody.toString().getBytes("UTF-8"), "multipart/form-data; boundary=---1234");
}
}