/**
* 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.capture.admin.impl;
import static org.junit.Assert.fail;
import static org.opencastproject.capture.admin.api.AgentState.IDLE;
import static org.opencastproject.capture.admin.api.AgentState.OFFLINE;
import static org.opencastproject.capture.admin.api.AgentState.UNKNOWN;
import static org.opencastproject.capture.admin.api.RecordingState.CAPTURING;
import static org.opencastproject.capture.admin.api.RecordingState.UPLOADING;
import static org.opencastproject.capture.admin.api.RecordingState.UPLOAD_FINISHED;
import org.opencastproject.capture.CaptureParameters;
import org.opencastproject.capture.admin.api.Agent;
import org.opencastproject.capture.admin.api.Recording;
import org.opencastproject.security.api.DefaultOrganization;
import org.opencastproject.security.api.JaxbRole;
import org.opencastproject.security.api.JaxbUser;
import org.opencastproject.security.api.SecurityService;
import org.opencastproject.security.api.User;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.persistence.PersistenceUtil;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.BundleContext;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.component.ComponentContext;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class CaptureAgentStateServiceImplTest {
private CaptureAgentStateServiceImpl service = null;
private Properties capabilities;
private static BundleContext bundleContext;
private static ComponentContext cc;
@Before
public void setUp() throws Exception {
setupService();
capabilities = new Properties();
capabilities.setProperty(CaptureParameters.CAPTURE_DEVICE_PREFIX + "CAMERA", "/dev/video0");
capabilities.setProperty(CaptureParameters.CAPTURE_DEVICE_PREFIX + "SCREEN", "/dev/video1");
capabilities.setProperty(CaptureParameters.CAPTURE_DEVICE_PREFIX + "AUDIO", "hw:0");
capabilities.setProperty(CaptureParameters.CAPTURE_DEVICE_NAMES, "CAMERA,SCREEN,AUDIO");
}
private void setupCC() {
String configKey = CaptureAgentStateServiceImpl.CAPTURE_AGENT_TIMEOUT_KEY;
String configValue = "15";
bundleContext = EasyMock.createNiceMock(BundleContext.class);
EasyMock.expect(bundleContext.getProperty(configKey)).andReturn(configValue).anyTimes();
EasyMock.replay(bundleContext);
cc = EasyMock.createNiceMock(ComponentContext.class);
EasyMock.expect(cc.getBundleContext()).andReturn(bundleContext);
EasyMock.replay(cc);
}
private void setupService() throws Exception {
service = new CaptureAgentStateServiceImpl();
service.setEntityManagerFactory(PersistenceUtil.newTestEntityManagerFactory(CaptureAgentStateServiceImpl.PERSISTENCE_UNIT));
DefaultOrganization organization = new DefaultOrganization();
HashSet<JaxbRole> roles = new HashSet<JaxbRole>();
roles.add(new JaxbRole(DefaultOrganization.DEFAULT_ORGANIZATION_ADMIN, organization, ""));
User user = new JaxbUser("testuser", "test", organization, roles);
SecurityService securityService = EasyMock.createNiceMock(SecurityService.class);
EasyMock.expect(securityService.getUser()).andReturn(user).anyTimes();
EasyMock.expect(securityService.getOrganization()).andReturn(new DefaultOrganization()).anyTimes();
EasyMock.replay(securityService);
service.setSecurityService(securityService);
setupCC();
service.activate(cc);
service.setupAgentCache(1, TimeUnit.HOURS);
}
@After
public void tearDown() {
service.deactivate();
}
@Test
public void nonExistantAgent() {
try {
service.getAgent("doesNotExist");
Assert.fail("Agent has been found");
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
}
@Test
public void noAgents() {
Assert.assertEquals(0, service.getKnownAgents().size());
}
@Test
public void badAgentStates() throws NotFoundException {
try {
service.setAgentState(null, "something");
Assert.assertEquals(0, service.getKnownAgents().size());
Assert.fail("IllegalArgument not thrown!");
} catch (IllegalArgumentException e) {
Assert.assertNotNull(e);
}
try {
Assert.assertEquals(0, service.getKnownAgents().size());
service.setAgentState("", "something");
Assert.fail("IllegalArgument not thrown!");
} catch (IllegalArgumentException e) {
Assert.assertNotNull(e);
}
try {
Assert.assertEquals(0, service.getKnownAgents().size());
service.setAgentState("something", null);
Assert.fail("IllegalArgument not thrown!");
} catch (IllegalArgumentException e) {
Assert.assertNotNull(e);
}
}
@Test
public void badAgentCapabilities() {
try {
service.setAgentConfiguration(null, capabilities);
Assert.fail("Null agent name accepted");
} catch (IllegalArgumentException e) {
Assert.assertNotNull(e);
}
Assert.assertEquals(0, service.getKnownAgents().size());
try {
service.setAgentConfiguration("", capabilities);
Assert.fail("Empty agent name accepted");
} catch (IllegalArgumentException e) {
Assert.assertNotNull(e);
}
Assert.assertEquals(0, service.getKnownAgents().size());
try {
service.setAgentState("something", null);
Assert.fail("Null agent state accepted");
} catch (IllegalArgumentException e) {
Assert.assertNotNull(e);
}
Assert.assertEquals(0, service.getKnownAgents().size());
}
private void verifyAgent(String name, String state, Properties caps) {
try {
Agent agent = service.getAgent(name);
Assert.assertEquals(name, agent.getName());
Assert.assertEquals(state, agent.getState());
Assert.assertEquals(caps, agent.getCapabilities());
} catch (NotFoundException e) {
if (state != null)
Assert.fail();
}
}
@Test
public void oneAgentState() {
service.setAgentState("agent1", IDLE);
Assert.assertEquals(1, service.getKnownAgents().size());
verifyAgent("notAgent1", null, null);
verifyAgent("agent1", IDLE, new Properties());
service.setAgentState("agent1", CAPTURING);
Assert.assertEquals(1, service.getKnownAgents().size());
verifyAgent("notAgent1", null, null);
verifyAgent("agent1", CAPTURING, new Properties());
}
@Test
public void oneAgentCapabilities() {
service.setAgentConfiguration("agent1", capabilities);
Assert.assertEquals(1, service.getKnownAgents().size());
verifyAgent("notAgent1", null, null);
verifyAgent("agent1", UNKNOWN, capabilities);
service.setAgentState("agent1", IDLE);
Assert.assertEquals(1, service.getKnownAgents().size());
verifyAgent("notAgent1", null, null);
verifyAgent("agent1", IDLE, capabilities);
service.setAgentConfiguration("agent1", new Properties());
Assert.assertEquals(1, service.getKnownAgents().size());
verifyAgent("notAnAgent", null, null);
verifyAgent("agent1", IDLE, new Properties());
}
@Test
public void removeAgent() {
service.setAgentConfiguration("agent1", capabilities);
Assert.assertEquals(1, service.getKnownAgents().size());
service.setAgentConfiguration("agent2", capabilities);
service.setAgentState("agent2", UPLOADING);
verifyAgent("notAnAgent", null, capabilities);
verifyAgent("agent1", UNKNOWN, capabilities);
verifyAgent("agent2", UPLOADING, capabilities);
try {
service.removeAgent("agent1");
Assert.assertEquals(1, service.getKnownAgents().size());
verifyAgent("notAnAgent", null, capabilities);
verifyAgent("agent1", null, capabilities);
verifyAgent("agent2", UPLOADING, capabilities);
} catch (NotFoundException e) {
Assert.fail();
}
try {
service.removeAgent("notAnAgent");
Assert.fail();
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
Assert.assertEquals(1, service.getKnownAgents().size());
verifyAgent("notAnAgent", null, capabilities);
verifyAgent("agent1", null, capabilities);
verifyAgent("agent2", UPLOADING, capabilities);
}
@Test
public void agentCapabilities() {
try {
service.getAgentCapabilities("agent");
Assert.fail();
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
try {
service.getAgentCapabilities("NotAgent");
Assert.fail();
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
service.setAgentConfiguration("agent", capabilities);
Properties agentCapabilities;
try {
agentCapabilities = service.getAgentCapabilities("agent");
Assert.assertEquals(capabilities, agentCapabilities);
} catch (NotFoundException e) {
Assert.fail();
}
try {
service.getAgentCapabilities("NotAgent");
Assert.fail();
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
}
@Test
public void stickyAgents() throws Exception {
Assert.assertEquals(0, service.getKnownAgents().size());
Properties cap1 = new Properties();
cap1.put(CaptureParameters.CAPTURE_DEVICE_PREFIX + "key", "value");
cap1.put(CaptureParameters.CAPTURE_DEVICE_NAMES, "key");
Properties cap2 = new Properties();
cap2.put(CaptureParameters.CAPTURE_DEVICE_PREFIX + "foo", "bar");
cap2.put(CaptureParameters.CAPTURE_DEVICE_NAMES, "foo");
Properties cap3 = new Properties();
cap3.put(CaptureParameters.CAPTURE_DEVICE_PREFIX + "bam", "bam");
cap3.put(CaptureParameters.CAPTURE_DEVICE_NAMES, "bam");
// Setup the two agents and persist them
service.setAgentState("sticky1", IDLE);
service.setAgentConfiguration("sticky1", cap1);
service.setAgentState("sticky2", CAPTURING);
service.setAgentConfiguration("sticky2", cap2);
service.setAgentState("sticky3", UPLOADING);
service.setAgentConfiguration("sticky3", cap3);
// Make sure they're set right
Assert.assertEquals(cap1, service.getAgentCapabilities("sticky1"));
Assert.assertEquals(IDLE, service.getAgent("sticky1").getState());
Assert.assertEquals(cap2, service.getAgentCapabilities("sticky2"));
Assert.assertEquals(CAPTURING, service.getAgent("sticky2").getState());
Assert.assertEquals(cap3, service.getAgentCapabilities("sticky3"));
Assert.assertEquals(UPLOADING, service.getAgent("sticky3").getState());
try {
service.getAgentCapabilities("sticky4");
Assert.fail();
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
try {
service.getAgent("sticky4");
Assert.fail();
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
// Shut down the service completely
service.deactivate();
// Restart the service with the same configuration as before
setupCC();
service.activate(cc);
Assert.assertEquals(3, service.getKnownAgents().size());
// The agents should still be there
Assert.assertEquals(cap1, service.getAgentCapabilities("sticky1"));
Assert.assertEquals(IDLE, service.getAgent("sticky1").getState());
Assert.assertEquals(cap2, service.getAgentCapabilities("sticky2"));
Assert.assertEquals(CAPTURING, service.getAgent("sticky2").getState());
Assert.assertEquals(cap3, service.getAgentCapabilities("sticky3"));
Assert.assertEquals(UPLOADING, service.getAgent("sticky3").getState());
try {
service.getAgentCapabilities("sticky4");
Assert.fail();
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
try {
service.getAgent("sticky4");
Assert.fail();
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
}
@Test
public void nonExistantRecording() {
try {
service.getRecordingState("doesNotExist");
Assert.fail("Non existing recording has been found");
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
}
@Test
public void badRecordingData() {
try {
service.setRecordingState(null, CAPTURING);
Assert.fail();
} catch (IllegalArgumentException e) {
Assert.assertNotNull(e);
}
Assert.assertEquals(0, service.getKnownRecordings().size());
try {
service.setRecordingState("", IDLE);
Assert.fail();
} catch (IllegalArgumentException e) {
Assert.assertNotNull(e);
}
Assert.assertEquals(0, service.getKnownRecordings().size());
Assert.assertFalse(service.setRecordingState("something", "bad_state"));
Assert.assertEquals(0, service.getKnownRecordings().size());
}
@Test
public void noRecordings() {
Assert.assertEquals(0, service.getKnownRecordings().size());
}
private void verifyRecording(String id, String state) {
if (state == null) {
try {
service.getRecordingState(id);
Assert.fail("");
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
} else {
try {
Recording recording = service.getRecordingState(id);
Assert.assertEquals(id, recording.getID());
Assert.assertEquals(state, recording.getState());
} catch (NotFoundException e) {
Assert.fail("");
}
}
}
@Test
public void oneRecording() {
service.setRecordingState("Recording1", UPLOAD_FINISHED);
Assert.assertEquals(1, service.getKnownRecordings().size());
verifyRecording("notRecording1", null);
verifyRecording("Recording1", UPLOAD_FINISHED);
service.setRecordingState("Recording1", CAPTURING);
Assert.assertEquals(1, service.getKnownRecordings().size());
verifyRecording("notRecording1", null);
verifyRecording("Recording1", CAPTURING);
}
@Test
public void removeRecording() {
service.setRecordingState("Recording1", CAPTURING);
Assert.assertEquals(1, service.getKnownRecordings().size());
service.setRecordingState("Recording2", UPLOADING);
Assert.assertEquals(2, service.getKnownRecordings().size());
verifyRecording("notAnRecording", null);
verifyRecording("Recording1", CAPTURING);
verifyRecording("Recording2", UPLOADING);
try {
service.removeRecording("Recording1");
} catch (NotFoundException e) {
Assert.fail();
}
try {
service.removeRecording("asdfasdf");
Assert.fail();
} catch (NotFoundException e) {
Assert.assertNotNull(e);
}
Assert.assertEquals(1, service.getKnownRecordings().size());
verifyRecording("notAnRecording", null);
verifyRecording("Recording1", null);
verifyRecording("Recording2", UPLOADING);
}
@Test
public void testAgentVisibility() throws Exception {
// Create a new capture agent called "visibility"
String agentName = "visibility";
service.setAgentState(agentName, IDLE);
// Ensure we can see it
Assert.assertEquals(1, service.getKnownAgents().size());
// Set the roles allowed to use this agent
Set<String> roles = new HashSet<String>();
roles.add("a_role_we_do_not_have");
AgentImpl agent = (AgentImpl) service.getAgent(agentName);
agent.setSchedulerRoles(roles);
service.updateAgentInDatabase(agent);
// Since we are an organizational admin, we should still see the agent
Assert.assertEquals(1, service.getKnownAgents().size());
// Use a security service that identifies us as a non-administrative user
DefaultOrganization organization = new DefaultOrganization();
HashSet<JaxbRole> roleSet = new HashSet<JaxbRole>();
roleSet.add(new JaxbRole("ROLE_NOT_ADMIN", organization, ""));
User user = new JaxbUser("testuser", "test", organization, roleSet);
SecurityService securityService = EasyMock.createNiceMock(SecurityService.class);
EasyMock.expect(securityService.getUser()).andReturn(user).anyTimes();
EasyMock.expect(securityService.getOrganization()).andReturn(new DefaultOrganization()).anyTimes();
EasyMock.replay(securityService);
service.setSecurityService(securityService);
// Ensure we can no longer see the agent, since we don't have an administrative role
Assert.assertEquals(0, service.getKnownAgents().size());
// TODO: Do we need to enforce access strictly? If someone asks for an agent by name, but they do not have the
// appropriate scheduler role, should we throw UnauthorizedException?
}
@Test
public void testManagedServiceFactory() throws Exception {
// Make sure we can register a capture agent with specific scheduler roles
String pid = UUID.randomUUID().toString();
Dictionary<String, String> properties = new Hashtable<String, String>();
properties.put("id", "agent1");
properties.put("organization", DefaultOrganization.DEFAULT_ORGANIZATION_ID);
properties.put("url", "http://agent1:8080/");
properties.put("schedulerRoles", DefaultOrganization.DEFAULT_ORGANIZATION_ADMIN + ", SOME_OTHER_ROLE");
service.updated(pid, properties);
// If any of the three values are missing, we should throw
properties.remove("id");
try {
service.updated(pid, properties);
fail();
} catch (ConfigurationException e) {
// expected
}
}
@Test
public void testUpdatedTimeSinceLastUpdate() throws Exception {
//See MH-10031
String name = "agent1";
Long lastHeardFrom = 0L;
Agent agent = null;
service.setAgentState(name, IDLE);
agent = service.getAgent(name);
lastHeardFrom = agent.getLastHeardFrom();
service.setAgentState(name, CAPTURING);
agent = service.getAgent(name);
Assert.assertTrue(lastHeardFrom <= agent.getLastHeardFrom());
lastHeardFrom = agent.getLastHeardFrom();
service.setAgentState(name, IDLE);
agent = service.getAgent(name);
Assert.assertTrue(lastHeardFrom <= agent.getLastHeardFrom());
lastHeardFrom = agent.getLastHeardFrom();
service.setAgentState(name, IDLE);
agent = service.getAgent(name);
Assert.assertTrue(lastHeardFrom <= agent.getLastHeardFrom());
lastHeardFrom = agent.getLastHeardFrom();
service.setAgentState(name, UNKNOWN);
agent = service.getAgent(name);
Assert.assertTrue(lastHeardFrom.equals(agent.getLastHeardFrom()));
}
@Test
public void testAgentStateTimeout() throws Exception {
service.setupAgentCache(1, TimeUnit.SECONDS);
String name = "agent1";
Long lastHeardFrom = 0L;
Agent agent = null;
service.setAgentState(name, IDLE);
agent = service.getAgent(name);
Assert.assertTrue(lastHeardFrom <= agent.getLastHeardFrom());
Assert.assertTrue(agent.getLastHeardFrom() <= System.currentTimeMillis());
Thread.sleep(1500);
Assert.assertEquals(OFFLINE, service.getAgentState(name));
}
@Test
public void testAllAgentsStateTimeout() throws Exception {
service.setupAgentCache(1, TimeUnit.SECONDS);
String name = "agent1";
Long lastHeardFrom = 0L;
Agent agent = null;
service.setAgentState(name, IDLE);
agent = service.getAgent(name);
Assert.assertTrue(lastHeardFrom <= agent.getLastHeardFrom());
Assert.assertTrue(agent.getLastHeardFrom() <= System.currentTimeMillis());
Thread.sleep(1500);
Map<String, Agent> agents = service.getKnownAgents();
Assert.assertEquals(OFFLINE, agents.get(name).getState());
}
@Test
public void testAgentReturn() throws Exception {
service.setupAgentCache(1, TimeUnit.SECONDS);
String name = "agent1";
Long lastHeardFrom = 0L;
Agent agent = null;
service.setAgentState(name, IDLE);
agent = service.getAgent(name);
Assert.assertTrue(lastHeardFrom <= agent.getLastHeardFrom());
Assert.assertTrue(agent.getLastHeardFrom() <= System.currentTimeMillis());
Thread.sleep(1500);
Map<String, Agent> agents = service.getKnownAgents();
Assert.assertEquals(OFFLINE, agents.get(name).getState());
Assert.assertEquals(OFFLINE, service.getAgentState(name));
service.setAgentState(name, IDLE);
long time = System.currentTimeMillis();
agent = service.getAgent(name);
Assert.assertTrue(lastHeardFrom <= agent.getLastHeardFrom());
Assert.assertTrue(time - agent.getLastHeardFrom() <= 5);
}}