diff --git a/.gitignore b/.gitignore index 482469d82..df32077bd 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +**/bin target/ .settings/ .project/ @@ -15,3 +16,4 @@ target/ test*.properties Scripts/ /.metadata/ +packaging/src diff --git a/DEVELOPER-README.md b/DEVELOPER-README.md index 2a9ef52cb..fb2e3a953 100644 --- a/DEVELOPER-README.md +++ b/DEVELOPER-README.md @@ -105,6 +105,7 @@ In addition, an additional profile for Metalnx is required, like this... localhost webdriver.chrome.driver C:/Users/pateldes/driver/chromedriver.exe + true diff --git a/README.md b/README.md index 7f95b9cb9..59045af7f 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ![Metalnx Logo](docs/IMAGES/mlx_logo_blue.png) ## Version: 4.2.1.0-SNAPSHOT -## Git Tag: niehs/issue2 +## Git Tag: niehs/development ## Date: Oct 27, 2017 @@ -87,6 +87,14 @@ The current Selenium tests have been refactored to start with basic health check Updated Jargon and controller code to gracefully handle no permission errors with a helpful message and a return to the previous directory view -#### NIEHS identified misc theming issues +#### Add properties based global control of features targeted at first towards removing tickets niehs #52 + +Add a global config to turn on/off certain features via metalnx.properties. This allows sites to globally turn off features such as tickets. + +#### NIEHS identified misc theming issues * #22 fix search text + +* #25 search - default to 'contains' + +* #11 Consider removing Jquery data table search filter as confusing next to the planned global search diff --git a/etc/irods-ext/metalnx.properties b/etc/irods-ext/metalnx.properties index f65d3e06b..a75bbe09d 100644 --- a/etc/irods-ext/metalnx.properties +++ b/etc/irods-ext/metalnx.properties @@ -69,3 +69,8 @@ msi.irods.list=libmsisync_to_archive.so,libmsi_update_unixfilesystem_resource_fr msi.irods.42.list=libmsisync_to_archive.so,libmsi_update_unixfilesystem_resource_free_space.so msi.other.list= + +###################################### +# global feature flags +# controls access to features globally +metalnx.enable.tickets=false diff --git a/packaging/src/.gitignore b/packaging/src/.gitignore new file mode 100644 index 000000000..ec484d1fd --- /dev/null +++ b/packaging/src/.gitignore @@ -0,0 +1 @@ +test*.properties diff --git a/packaging/src/emc-metalnx-core/attic/TestCreateTicketWithGroupRestriction.java b/packaging/src/emc-metalnx-core/attic/TestCreateTicketWithGroupRestriction.java new file mode 100755 index 000000000..75e43c699 --- /dev/null +++ b/packaging/src/emc-metalnx-core/attic/TestCreateTicketWithGroupRestriction.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCreateTicketWithGroupRestriction { + private static final String PUBLIC_GROUP = "public"; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + private DataGridTicket dgt; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + + dgt = new DataGridTicket(targetPath); + dgt.addGroup(PUBLIC_GROUP); + } + + @After + public void tearDown() throws JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testCreateTicketWithHostRestriction() throws DataGridConnectionRefusedException, + DataGridTicketException, JargonException { + ticketString = ticketService.create(dgt); + List groups = ticketUtils.listAllGroupRestrictionsForSpecifiedTicket(ticketString); + + assertEquals(1, groups.size()); + assertTrue(groups.contains(PUBLIC_GROUP)); + } +} diff --git a/packaging/src/emc-metalnx-core/attic/TestDownloadWithTicket.java b/packaging/src/emc-metalnx-core/attic/TestDownloadWithTicket.java new file mode 100755 index 000000000..06b4fa365 --- /dev/null +++ b/packaging/src/emc-metalnx-core/attic/TestDownloadWithTicket.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketDownloadException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.Stream2StreamAO; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestDownloadWithTicket { + private static final String FILE_CONTENT = "Test for ticket"; + private static final int BUFFER_SIZE = 4 * 1024 * 1024; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private TicketClientService ticketClientService; + + private String targetPath, filePath, ticketString; + private TestTicketUtils ticketUtils; + private File localFile, fileFromIRods; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + localFile = ticketUtils.createLocalFile(); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.READ); + uploadFileToIRODS(targetPath, localFile); + filePath = String.format("%s/%s", targetPath, localFile.getName()); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile); + FileUtils.deleteQuietly(fileFromIRods); + ticketUtils.deleteTicket(ticketString); + ticketUtils.deleteIRODSFile(filePath); + } + + @Test + public void testDownloadFileUsingATicket() throws DataGridTicketDownloadException, DataGridTicketInvalidUserException, + IOException { + fileFromIRods = ticketClientService.getFileFromIRODSUsingTicket(ticketString, filePath); + assertNotNull(fileFromIRods); + assertEquals(TestTicketUtils.TICKET_FILE_CONTENT, FileUtils.readFileToString(fileFromIRods, StandardCharsets.UTF_8.name())); + } + + private void uploadFileToIRODS(String path, File file) throws DataGridConnectionRefusedException, JargonException, + IOException { + IRODSFile targetFile = irodsServices.getIRODSFileFactory().instanceIRODSFile(path, file.getName()); + + if (targetFile.exists()) { + return; + } + + Stream2StreamAO streamAO = irodsServices.getStream2StreamAO(); + InputStream inputStream = new FileInputStream(file); + streamAO.transferStreamToFileUsingIOStreams(inputStream, (File) targetFile, 0, BUFFER_SIZE); + inputStream.close(); + targetFile.close(); + } +} diff --git a/packaging/src/emc-metalnx-core/attic/TestResourceService.java b/packaging/src/emc-metalnx-core/attic/TestResourceService.java new file mode 100755 index 000000000..593b41f14 --- /dev/null +++ b/packaging/src/emc-metalnx-core/attic/TestResourceService.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.resource; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.ResourceService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.Date; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test Resource service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestResourceService { + + public static final String RODSADMIN = "rodsadmin"; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${irods.host}") + private String host; + + @Autowired + private ResourceService resourceService; + + private String parentRescName, childRescName; + + private DataGridResource parentResc, childResc; + + @Before + public void setUp() throws DataGridConnectionRefusedException { + long time = System.currentTimeMillis(); + parentRescName = "testResc" + time; + childRescName = "testRescChild" + time; + Date date = new Date(); + + parentResc = new DataGridResource(); + parentResc.setName(parentRescName); + parentResc.setType("compound"); + parentResc.setZone(zone); + parentResc.setCreateTime(date); + parentResc.setModifyTime(date); + parentResc.setFreeSpaceDate(date); + parentResc.setPath("/var/lib/irods/iRODS/Vault2"); + parentResc.setHost(host); + + childResc = new DataGridResource(); + childResc.setName(childRescName); + childResc.setType("unixfilesystem"); + childResc.setZone(zone); + childResc.setCreateTime(date); + childResc.setModifyTime(date); + childResc.setFreeSpaceDate(date); + childResc.setPath("/var/lib/irods/iRODS/Vault2"); + childResc.setHost(host); + + resourceService.createResource(parentResc); + } + + @After + public void tearDown() throws DataGridConnectionRefusedException { + if (resourceService.find(parentRescName) != null) + resourceService.deleteResource(parentRescName); + + if (resourceService.find(childRescName) != null) + resourceService.deleteResource(childRescName); + } + + @Test + public void testDeleteResourceByName() { + assertTrue(resourceService.deleteResource(parentRescName)); + } + + @Test + public void testDeleteResourceWithChildren() throws DataGridConnectionRefusedException { + resourceService.createResource(childResc); + resourceService.addChildToResource(parentRescName, childRescName); + + assertTrue(resourceService.deleteResource(parentRescName)); + assertNotNull(resourceService.find(childRescName)); + } + + +} diff --git a/packaging/src/emc-metalnx-core/attic/TestRuleDeploymentService.java b/packaging/src/emc-metalnx-core/attic/TestRuleDeploymentService.java new file mode 100755 index 000000000..efd48a3c5 --- /dev/null +++ b/packaging/src/emc-metalnx-core/attic/TestRuleDeploymentService.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.rules; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; +import com.emc.metalnx.services.interfaces.*; +import com.emc.metalnx.services.irods.ResourceServiceImpl; +import com.emc.metalnx.services.irods.RuleDeploymentServiceImpl; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.HashMap; +import java.util.List; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; + +/** + * Test for Rule Service + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestRuleDeploymentService { + private static final String TEST_RULE_NAME = "test_rule_deployment.re"; + private static final String RULE_CACHE_DIR = ".rulecache"; + public static final String RULE_FILE_EXTENSION = ".re"; + + @Autowired + private CollectionService collectionService; + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private ConfigService configService; + + @Spy + private ResourceService resourceService = new ResourceServiceImpl(); + + @Mock + private RuleService ruleService; + + @InjectMocks + private RuleDeploymentService ruleDeploymentService; + + private MockMultipartFile file; + private String ruleCachePath; + + @Before + public void setUp() throws DataGridRuleException, DataGridConnectionRefusedException, JargonException { + ruleCachePath = String.format("/%s/%s", configService.getIrodsZone(), RULE_CACHE_DIR); + + ruleDeploymentService = spy(RuleDeploymentServiceImpl.class); // partial mocking + + MockitoAnnotations.initMocks(this); + + ReflectionTestUtils.setField(ruleDeploymentService, "irodsServices", irodsServices); + ReflectionTestUtils.setField(ruleDeploymentService, "configService", configService); + + removeRuleCacheColl(); + createRuleCacheColl(); + + file = new MockMultipartFile(TEST_RULE_NAME, "Hello World".getBytes()); + + when(resourceService.find(anyString())).thenCallRealMethod(); + when(ruleService.executeRule(anyString())).thenReturn(new HashMap<>()); + } + + @After + public void tearDown() throws DataGridConnectionRefusedException, JargonException { + removeRuleCacheColl(); + } + + @Test + public void testDeployRule() throws DataGridException { + ArgumentCaptor argCaptor = ArgumentCaptor.forClass(String.class); + ruleDeploymentService.deployRule(file); + verify(ruleService).execDeploymentRule(argCaptor.capture(), argCaptor.capture(), argCaptor.capture()); + assertFalse(argCaptor.getAllValues().get(1).endsWith(RULE_FILE_EXTENSION)); + + List items = + collectionService.getSubCollectionsAndDataObjetsUnderPath(ruleCachePath); + + boolean ruleInCache = false; + for(DataGridCollectionAndDataObject item: items) { + if(TEST_RULE_NAME.equals(item.getName())) { + ruleInCache = true; + break; + } + } + + assertTrue(ruleInCache); + } + + /** + * Create the rule cache collection in the grid + * @throws DataGridConnectionRefusedException + * @throws JargonException + */ + private void createRuleCacheColl() throws DataGridConnectionRefusedException, JargonException { + IRODSFileFactory iff = irodsServices.getIRODSFileFactory(); + IRODSFile ruleCacheColl = iff.instanceIRODSFile("/" + configService.getIrodsZone(), RULE_CACHE_DIR); + irodsServices.getIRODSFileSystemAO().mkdir(ruleCacheColl, false); + } + + /** + * Removes the rule cache collection from the grid + * @throws JargonException + * @throws DataGridConnectionRefusedException + */ + private void removeRuleCacheColl() throws JargonException, DataGridConnectionRefusedException { + IRODSFile collectionToBeRemoved = irodsServices.getIRODSFileFactory().instanceIRODSFile(ruleCachePath); + if(collectionToBeRemoved.exists()) { + irodsServices.getIRODSFileSystemAO().directoryDeleteForce(collectionToBeRemoved); + } + } +} diff --git a/packaging/src/emc-metalnx-core/attic/TestTicketAuthenticatedAccess.java b/packaging/src/emc-metalnx-core/attic/TestTicketAuthenticatedAccess.java new file mode 100755 index 000000000..2538c82d0 --- /dev/null +++ b/packaging/src/emc-metalnx-core/attic/TestTicketAuthenticatedAccess.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.ticketclient; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.connection.IRODSAccount; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.domain.DataObject; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.auth.UserTokenDetails; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import com.emc.metalnx.services.tests.tickets.TestTicketUtils; + +import junit.framework.Assert; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestTicketAuthenticatedAccess { + private static final String RESOURCE = "demoResc"; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${irods.host}") + private String host; + + @Value("${irods.port}") + private String port; + + @Value("${jobs.irods.username}") + private String username; + + @Value("${jobs.irods.password}") + private String password; + + @Autowired + private TicketClientService ticketClientService; + + @Autowired + private IRODSServices irodsServices; + + private String ticketString, targetPath, filePath; + private TestTicketUtils ticketUtils; + private File localFile; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + localFile = ticketUtils.createLocalFile(); + filePath = String.format("%s/%s", targetPath, localFile.getName()); + + IRODSAccount authIrodsAccount = IRODSAccount.instance(host, Integer.valueOf(port), username, password, + targetPath, zone, RESOURCE); + + UserTokenDetails userTokenDetails = Mockito.mock(UserTokenDetails.class); + + Authentication authentication = Mockito.mock(Authentication.class); + SecurityContext securityContext = Mockito.mock(SecurityContext.class); + SecurityContextHolder.setContext(securityContext); + Mockito.when(securityContext.getAuthentication()).thenReturn(authentication); + Mockito.when(authentication.getDetails()).thenReturn(userTokenDetails); + Mockito.when(userTokenDetails.getIrodsAccount()).thenReturn(authIrodsAccount); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile); + ticketUtils.deleteIRODSFile(filePath); + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testTransferFileWithTicketAsAuthenticatedUser() throws DataGridTicketUploadException, + DataGridTicketInvalidUserException, DataGridConnectionRefusedException, JargonException { + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile, targetPath); + DataObject obj = irodsServices.getDataObjectAO().findByCollectionNameAndDataName(targetPath, + localFile.getName()); + Assert.assertEquals(username, obj.getDataOwnerName()); + } +} diff --git a/packaging/src/emc-metalnx-core/attic/TestTicketWithGroupRestriction.java b/packaging/src/emc-metalnx-core/attic/TestTicketWithGroupRestriction.java new file mode 100755 index 000000000..81f87e196 --- /dev/null +++ b/packaging/src/emc-metalnx-core/attic/TestTicketWithGroupRestriction.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.ticketclient; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import com.emc.metalnx.services.tests.tickets.TestTicketUtils; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.io.File; +import java.io.IOException; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestTicketWithGroupRestriction { + private static final String PUBLIC_GROUP = "public"; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketClientService ticketClientService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString, filePath; + private TestTicketUtils ticketUtils; + private File localFile; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + ticketUtils.addGroupRestriction(ticketString, PUBLIC_GROUP); + localFile = ticketUtils.createLocalFile(); + filePath = String.format("%s/%s", targetPath, localFile.getName()); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile); + ticketUtils.deleteTicket(ticketString); + ticketUtils.deleteIRODSFile(filePath); + } + + @Test(expected = DataGridTicketUploadException.class) + public void testTicketWithGroupRestriction() throws DataGridTicketUploadException, DataGridTicketInvalidUserException { + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile, targetPath); + } +} diff --git a/packaging/src/emc-metalnx-core/attic/TestUploadWithTicket.java b/packaging/src/emc-metalnx-core/attic/TestUploadWithTicket.java new file mode 100755 index 000000000..342559e88 --- /dev/null +++ b/packaging/src/emc-metalnx-core/attic/TestUploadWithTicket.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.io.File; +import java.io.IOException; + +import static junit.framework.Assert.assertTrue; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestUploadWithTicket { + private static final String TEST_FILE_NAME = "test-ticket.txt"; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private TicketClientService ticketClientService; + + private String targetPath, filePath, ticketString; + private TestTicketUtils ticketUtils; + private File localFile; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + localFile = ticketUtils.createLocalFile(); + filePath = String.format("%s/%s", targetPath, localFile.getName()); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile); + ticketUtils.deleteTicket(ticketString); + ticketUtils.deleteIRODSFile(filePath); + } + + @Test + public void testUploadFileUsingATicket() throws DataGridConnectionRefusedException, + DataGridTicketUploadException, DataGridTicketInvalidUserException, JargonException { + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile, targetPath); + IRODSFile ticketIRODSFile = irodsServices.getIRODSFileFactory().instanceIRODSFile(filePath); + assertTrue(ticketIRODSFile.exists()); + } +} diff --git a/packaging/src/emc-metalnx-core/pom.xml b/packaging/src/emc-metalnx-core/pom.xml new file mode 100755 index 000000000..6e3a7afc5 --- /dev/null +++ b/packaging/src/emc-metalnx-core/pom.xml @@ -0,0 +1,272 @@ + + + + 4.0.0 + + com.emc.metalnx + emc-metalnx + 1.4.0 + + emc-metalnx-core + + + javassist + javassist + + + org.hibernate + hibernate-core + + + org.hibernate + hibernate-validator + + + org.hibernate + hibernate-envers + + + org.springframework + spring-orm + + + org.springframework + spring-context + + + org.springframework + spring-tx + + + org.springframework.security + spring-security-web + + + org.springframework.security + spring-security-config + + + mysql + mysql-connector-java + + + postgresql + postgresql + + + + + com.fasterxml.jackson.core + jackson-core + + + + com.fasterxml.jackson.core + jackson-annotations + + + + com.fasterxml.jackson.core + jackson-databind + + + + + + + + + org.codehaus.mojo + jaxb2-maven-plugin + 1.5 + + + + xjc + + + + + + -extension -npa -b "${project.basedir}/src/main/xsd/global.xjb" + + + + maven-antrun-plugin + + + + 0 + validate + + + + + + test.data.directory=${jargon.test.data.directory} + test.irods.admin=${jargon.test.irods.admin} + test.irods.admin.password=${jargon.test.irods.admin.password} + test.irods.user=${jargon.test.irods.user} + test.irods.password=${jargon.test.irods.password} + test.irods.resource=${jargon.test.irods.resource} + test2.irods.user=${jargon.test.irods.user2} + test2.irods.password=${jargon.test.irods.password2} + test2.irods.resource=${jargon.test.irods.resource2} + test3.irods.user=${jargon.test.irods.user3} + test3.irods.password=${jargon.test.irods.password3} + test3.irods.resource=${jargon.test.irods.resource3} + test.irods.host=${jargon.test.irods.host} + test.resource.host=${jargon.test.resource.host} + test.irods.port=${jargon.test.irods.port} + test.irods.zone=${jargon.test.irods.zone} + jargon.test.kerberos.user=${jargon.test.kerberos.user} + jargon.test.user.group=${jargon.test.user.group} + test.resource.group=${jargon.test.resource.group} + test.irods.userDN=${jargon.test.irods.userDN} + test.irods.scratch.subdir=${jargon.test.irods.scratch.subdir} + test.option.exercise.remoteexecstream=${jargon.test.option.exercise.remoteexecstream} + test.option.eirods=${test.option.eirods} + test.option.exercise.audit=${jargon.test.option.exercise.audit} + test.option.exercise.workflow=${jargon.test.option.exercise.workflow} + test.option.exercise.filesystem.mount=${jargon.test.option.exercise.filesystem.mount} + test.option.exercise.filesystem.mount.local=${jargon.test.option.exercise.filesystem.mount.local} + test.option.distributed.resources=${test.option.distributed.resources} + test.option.registration=${test.option.registration} + test.option.strictACL=${test.option.strictACL} + test.option.federated.zone=${test.option.federated.zone} + test.option.kerberos=${test.option.kerberos} + test.option.pam=${test.option.pam} + test.option.ssl.configured=${test.option.ssl.configured} + jargon.test.pam.user=${jargon.test.pam.user} + jargon.test.pam.password=${jargon.test.pam.password} + test.federated.irods.admin=${jargon.test.federated.irods.admin} + test.federated.irods.admin.password=${jargon.test.federated.irods.admin.password} + test.federated.irods.user=${jargon.test.federated.irods.user} + test.federated.irods.password=${jargon.test.federated.irods.password} + test.federated.irods.resource=${jargon.test.federated.irods.resource} + test.federated.irods.host=${jargon.test.federated.irods.host} + test.federated.irods.port=${jargon.test.federated.irods.port} + test.federated.irods.zone=${jargon.test.federated.irods.zone} + test.option.gsi=${test.option.gsi} + test.option.gsi.host=${test.option.gsi.host} + test.option.gsi.port=${test.option.gsi.port} + test.option.gsi.zone=${test.option.gsi.zone} + test.option.gsi.dn=${test.option.gsi.dn} + test.option.gsi.user=${test.option.gsi.user} + test.option.gsi.file=${test.option.gsi.file} + test.option.mount.basedir=${test.option.mount.basedir} + test.option.python=${test.option.python} + + + + + run + + + + + 2 + validate + + + + + + irods.host=${jargon.test.irods.host} + irods.port=${jargon.test.irods.port} + irods.zoneName=${jargon.test.irods.zone} + irods.admin.user=${jargon.test.irods.admin} + irods.admin.password=${jargon.test.irods.admin.password} + + + irods.auth.scheme=${metalnx.auth.scheme} + default.storage.resource=${jargon.test.irods.resource} + ssl.negotiation.policy=${metalnx.ssl.policy} + + ########################################################## + + utilize.packing.streams=${metalnx.packing.streams} + + + compute.checksum=${metalnx.compute.checksum} + + ########################################################## + + db.driverClassName=${metalnx.jdbc.driver} + db.url=${metalnx.jdbc.url} + db.username=${metalnx.jdbc.user} + db.password=${metalnx.jdbc.password} + hibernate.dialect=${metalnx.jdbc.dialect} + + + hibernate.show_sql=true + hibernate.format_sql=false + + + hibernate.hbm2ddl.auto=update + + + connection.pool_size=5 + + ###################################### + + jobs.irods.username=${jargon.test.irods.admin} + jobs.irods.password=${jargon.test.irods.admin.password} + jobs.irods.auth.scheme=${metalnx.auth.scheme} + runSyncJobs=true + + + rmd.connection.timeout=500 + rmd.connection.port=8000 + + reverse.dns.lookup=false + + ###################################### + + populate.msi.enabled=false + illumina.msi.enabled=true + + msi.api.version=1.X.X + + msi.metalnx.list=libmsiget_illumina_meta.so,libmsiobjget_microservices.so,libmsiobjget_version.so,libmsiobjjpeg_extract.so,libmsiobjput_mdbam.so,libmsiobjput_mdbam.so,libmsiobjput_mdmanifest.so,libmsiobjput_mdvcf.so,libmsiobjput_populate.so + + msi.irods.list=libmsisync_to_archive.so,libmsi_update_unixfilesystem_resource_free_space.so,libmsiobjput_http.so,libmsiobjput_irods.so,libmsiobjget_irods.so,libmsiobjget_http.so,libmsiobjput_slink.so,libmsiobjget_slink.so + + msi.irods.42.list=libmsisync_to_archive.so,libmsi_update_unixfilesystem_resource_free_space.so + + msi.other.list= + + resource.location.images=/images/,classpath:static/images/ + resource.location.fonts=/fonts/,classpath:static/fonts/ + resource.location.css=/css/,classpath:static/css/ + resource.location.js=/js/,classpath:static/js/ + resource.location.i18=classpath:i18n/messages + resource.location.i18-users=classpath:i18n-users/messages + + + + + + run + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/FavoriteDao.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/FavoriteDao.java new file mode 100755 index 000000000..3a13bc45d --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/FavoriteDao.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserFavorite; + +import java.util.List; + +public interface FavoriteDao { + + /** + * Retrieve a given Favorite based on the user and path + * + * @param user + * @param path + * @return a {@link DataGridUserFavorite} + */ + DataGridUserFavorite findByUserAndPath(DataGridUser user, String path); + + /** + * Add a favorite to a user using the path and user entity + * + * @param user + * data grid user to add the favorite to + * @param path + * path to the collection/data object to be added as a favorite + * @param isCollection + * indicates whether a path is a collection or a file + * @return a confirmation that the insertion has been successfully + */ + Long addByUserAndPath(DataGridUser user, String path, boolean isCollection); + + /** + * Removes a favorite based on the path and the user + * + * @param user + * @param path + * @return a confirmation that the deletion has been successful + */ + boolean removeByUserAndPath(DataGridUser user, String path); + + /** + * Removes a favorite based on the user + * + * @param {@link DataGridUser} user + * @return a confirmation that the deletion has been successful + */ + boolean removeByUser(DataGridUser user); + + /** + * Retrieve a given Favorite based on the user + * + * @param user + * @return list of {@link DataGridUserFavorite} + */ + List findByUser(DataGridUser user); + + /** + * Retrieves all the favorites on a given path + * + * @param path + * @return list of {@link DataGridUserFavorite} + */ + List findFavoritesByPath(String path); + + /** + * Removes a favorite based on the given path + * + * @param path + * path to remove any favorite + * @return a confirmation that the deletion has been successful + */ + boolean removeByPath(String path); + + /** + * Removes all existing favorites whose parent path is the given path. Basically, if the + * following favorites exist: + * a/b/c + * a/b/c/d + * x/y/z/a/b/c + * and the directory "a" gets deleted. Both "a/b/c" and "a/b/c/d" should be removed from + * favorites since they no longer exist. But "x/y/z/a/b/c" should be kept. + * + * @param parentPath + * path to remove any favorite + * @return a confirmation that the deletion has been successful + */ + boolean removeByParentPath(String parentPath); + + /** + * Find list of favorites paginated + * + * @param user + * @param offset + * represents the starting row in the query + * @param limit + * how many elements will have the result + * @param searchString + * is different from null if filter is used + * @param orderBy + * order by a column + * @param orderDir + * the direction of the order can be 'desc' or 'asc' + * @param onlyCollections + * indicates if results should contain only collections + * @return list of {@link DataGridUserFavorite} + */ + List findByUserPaginated(DataGridUser user, int offset, int limit, String searchString, String orderBy, String orderDir, + boolean onlyCollections); + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/GroupBookmarkDao.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/GroupBookmarkDao.java new file mode 100755 index 000000000..7a4df24b9 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/GroupBookmarkDao.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao; + +import com.emc.metalnx.core.domain.dao.generic.GenericDao; +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridGroupBookmark; + +import java.util.List; + +public interface GroupBookmarkDao extends GenericDao { + + /** + * Add a bookmark to a group using the path and group entity + * + * @param group + * @param path + * @param isCollection + * @return a confirmation that the insertion has been successfully + */ + public Long addByGroupAndPath(DataGridGroup group, String path, boolean isCollection); + + /** + * Removes a bookmark based on the path and the group + * + * @param group + * @param path + * @return a confirmation that the deletion has been successfully + */ + public boolean removeByGroupAndPath(DataGridGroup group, String path); + + /** + * Removes a bookmark based on the group + * + * @param group + * @return a confirmation that the deletion has been successfully + */ + public boolean removeByGroup(DataGridGroup group); + + /** + * Retrieve a given GroupBookmark based on the group + * + * @param group + * @return a {@link DataGridGroupBookmark} + */ + public List findByGroup(DataGridGroup group); + + /** + * Retrieves all the bookmarks on a given path + * + * @param path + * @return list of {@link DataGridGroupBookmark} + */ + public List findBookmarksByPath(String path); + + /** + * Removes a bookmark based on the given path + * + * @param path + * path to remove any bookmark + * @return a confirmation that the deletion has been successful + */ + public boolean removeByPath(String path); + + /** + * Removes all existing bookmarks whose parent path is the given path. Basically, if the + * following bookmarks exist: + * a/b/c + * a/b/c/d + * x/y/z/a/b/c + * and the directory "a" gets deleted. Both "a/b/c" and "a/b/c/d" should be removed from + * bookmarks since they no longer exist. But "x/y/z/a/b/c" should be kept. + * + * @param parentPath + * path to remove any bookmark + * @return a confirmation that the deletion has been successful + */ + public boolean removeByParentPath(String parentPath); + + /** + * Find group bookmarks with limits and filter for pagination + * + * @param groupIds + * @param offset + * @param limit + * @param searchString + * @param orderBy + * @param orderDir + * @param onlyCollections + * @return + */ + public List findGroupBookmarksByGroupsIds(String[] groupIds, int offset, int limit, String searchString, String orderBy, + String orderDir, boolean onlyCollections); + + /** + * Gives the total number of group bookmarks for a list of groups + * + * @param groupIds + * @return + */ + public Long countGroupBookmarksByGroupsIds(String[] groupIds); + + /** + * Changes an existing bookmark to a new value. + * + * @param oldPath + * existing path that will be updated + * @param newPath + * new path + * @return True, if oldePath was successfully changed to newPath. False, otherwise. + */ + boolean updateBookmark(String oldPath, String newPath); +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/GroupDao.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/GroupDao.java new file mode 100755 index 000000000..c5597afcd --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/GroupDao.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao; + +import com.emc.metalnx.core.domain.dao.generic.GenericDao; +import com.emc.metalnx.core.domain.entity.DataGridGroup; + +import java.util.List; + +public interface GroupDao extends GenericDao { + + /** + * Find a group by its name + * @param groupname + * @return List of groups + */ + List findByGroupname(String groupname); + + /** + * Find group by group and zone + * @param groupname + * @param zone + * @return + */ + DataGridGroup findByGroupnameAndZone(String groupname, String zone); + + /** + * Deletes a group by its group + * @param groupname + * @return + */ + boolean deleteByGroupname(String groupname); + + /** + * Deletes a group by its id + * @param id + * @return true if a group whose id matches with the id parameter + */ + boolean deleteByDataGridGroupId(long id); + + /** + * Finds groups that match the specified query + * @param query + * @return list of groups + */ + public List findByQueryString(String query); + + /** + * Finds all groups that match the input Data Grid IDs. + * @param ids + * @return list of groups + */ + public List findByDataGridIdList(String[] ids); + + /** + * Finds all groups that match the input Data Grid Group names. + * @param ids + * @return list of group names + */ + public List findByGroupNameList(String[] groupNames); + + /** + * Finds all groups that match the input Data Grid IDs. + * @param ids + * @return list of groups + */ + public List findByIdList(Long[] ids); + + /** + * Finds a group that matches the input Data Grid ID. + * @param id + * @return DataGridGroup + */ + public DataGridGroup findByDataGridId(long id); +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/TemplateDao.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/TemplateDao.java new file mode 100755 index 000000000..ab3ef4376 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/TemplateDao.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao; + +import com.emc.metalnx.core.domain.dao.generic.GenericDao; +import com.emc.metalnx.core.domain.entity.DataGridTemplate; +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; + +import java.util.List; + +public interface TemplateDao extends GenericDao { + + /** + * Gets the template id based on its name (the template name given has to + * match exactly the template name existing in the database). + * + * @param templateName + * name of the template + * @return the template id if the template is found. Null, otherwise. + */ + long getTemplateId(String templateName); + + /** + * Find a template by its name + * + * @param templateName + * name of the template + * @return Template matching the name + */ + DataGridTemplate findByName(String templateName); + + /** + * Find a template by its id + * + * @param id + * template id + * @return Template if the id exists. Null, if the id was not found. + */ + DataGridTemplate findById(long id); + + /** + * Deletes a template by its id + * + * @param id + * id of the template to be removed + * @return true if the template was successfully removed. False, otherwise. + */ + boolean deleteById(long id); + + /** + * Finds templates by a query string + * + * @param query + * string containing the search term to match template names + * @return list of templates + */ + List findByQueryString(String query); + + /** + * Lists all fields existing in a template + * + * @param template + * name of the template + * @return List of template fields, if any + */ + List listTemplateFields(String template); + + /** + * Lists all fields existing in a template + * + * @param id + * id of the template + * @return List of template fields, if any + */ + List listTemplateFields(Long id); + + /** + * Returns all the public metadata templates + * + * @return List of template fields, if any + */ + List listPublicTemplates(); + + /** + * Returns all the private metadata templates + * + * @return List of template fields, if any + */ + List listPrivateTemplatesByUser(String user); + + @Override + /** + * Overrides the merge method in order to handle the version number of the + * current template. + */ + void merge(DataGridTemplate template); +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/TemplateFieldDao.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/TemplateFieldDao.java new file mode 100755 index 000000000..93b7ac046 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/TemplateFieldDao.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao; + +import com.emc.metalnx.core.domain.dao.generic.GenericDao; +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateAttrException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateUnitException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateValueException; + +public interface TemplateFieldDao extends GenericDao { + + /** + * Find a template field by its id + * + * @param id + * template field id + * @return Template if the id exists. Null, if the id was not found. + */ + DataGridTemplateField findById(long id); + + /** + * Modify a template field by its id + * + * @param id + * id of the template field to be removed + * @return true if the template field was successfully modified. False, otherwise. + * @throws DataGridTemplateAttrException + * @throws DataGridTemplateValueException + * @throws DataGridTemplateUnitException + */ + boolean modifyById(long id, String attribute, String value, String unit) throws DataGridTemplateAttrException, DataGridTemplateValueException, + DataGridTemplateUnitException; +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/UserBookmarkDao.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/UserBookmarkDao.java new file mode 100755 index 000000000..948b4d526 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/UserBookmarkDao.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao; + +import com.emc.metalnx.core.domain.dao.generic.GenericDao; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserBookmark; + +import java.util.List; + +public interface UserBookmarkDao extends GenericDao { + + /** + * Add a bookmark to a user using the path and user entity + * + * @param user + * data grid user to add the bookmark to + * @param path + * path to the collection/data object to be added as a bookmark + * @param isCollection + * indicates whether a path is a collection or a file + * @return a confirmation that the insertion has been successfully + */ + public Long addByUserAndPath(DataGridUser user, String path, boolean isCollection); + + /** + * Removes a bookmark based on the path and the user + * + * @param user + * @param path + * @return a confirmation that the deletion has been successful + */ + public boolean removeByUserAndPath(DataGridUser user, String path); + + /** + * Removes a bookmark based on the given path + * + * @param path + * path to remove any bookmark + * @return a confirmation that the deletion has been successful + */ + public boolean removeByPath(String path); + + /** + * Removes a bookmark based on the given user + * + * @param path + * path to remove any bookmark + * @return a confirmation that the deletion has been successful + */ + public boolean removeByUser(DataGridUser user); + + /** + * Removes all existing bookmarks whose parent path is the given path. Basically, if the + * following bookmarks exist: + * a/b/c + * a/b/c/d + * x/y/z/a/b/c + * and the directory "a" gets deleted. Both "a/b/c" and "a/b/c/d" should be removed from + * bookmarks since they no longer exist. But "x/y/z/a/b/c" should be kept. + * + * @param parentPath + * path to remove any bookmark + * @return a confirmation that the deletion has been successful + */ + public boolean removeByParentPath(String parentPath); + + /** + * Retrieve a given UserBookmark based on the user and path + * + * @param user + * @param path + * @return a {@link DataGridUserBookmark} + */ + public DataGridUserBookmark findByUserAndPath(DataGridUser user, String path); + + /** + * Retrieve a given UserBookmark based on the user + * + * @param user + * @return a {@link DataGridUserBookmark} + */ + public List findByUser(DataGridUser user); + + /** + * Retrieves all the bookmarks on a given path + * + * @param path + * @return list of {@link DataGridUserBookmark} + */ + public List findBookmarksByPath(String path); + + /** + * Find list of bookmarks paginated + * + * @param user + * @param start + * represents the starting row in the query + * @param length + * how many elements will have the result + * @param searchString + * is different from null if filter is used + * @param orderBy + * order by a column + * @param orderDir + * the direction of the order can be 'desc' or 'asc' + * @param onlyCollections + * indicates if the results should contain only collections + * @return list of {@link DataGridUserBookmark} + */ + public List findByUserPaginated(DataGridUser user, int start, int length, String searchString, String orderBy, + String orderDir, boolean onlyCollections); + + /** + * Find list of bookmarks paginated + * + * @param user + * @param start + * represents the starting row in the query + * @param length + * how many elements will have the result + * @param searchString + * is different from null if filter is used + * @param orderBy + * list of columns the database will order the results by + * @param orderDir + * list of directions (ASC or DESC) that will applied to the columns in the order by + * clause + * @param onlyCollections + * indicates if the results should contain only collections + * @return list of {@link DataGridUserBookmark} + */ + public List findByUserPaginated(DataGridUser user, int start, int length, String searchString, List orderBy, + List orderDir, boolean onlyCollections); + + /** + * Changes an existing bookmark to a new value. + * + * @param oldPath + * existing path that will be updated + * @param newPath + * new path + * @return True, if oldePath was successfully changed to newPath. False, otherwise. + */ + boolean updateBookmark(String oldPath, String newPath); +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/UserDao.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/UserDao.java new file mode 100755 index 000000000..090758468 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/UserDao.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao; + +import com.emc.metalnx.core.domain.dao.generic.GenericDao; +import com.emc.metalnx.core.domain.entity.DataGridUser; + +import java.util.List; + +public interface UserDao extends GenericDao { + + /** + * Find a user by his ID + * @param ids + * @return DataGridUser + */ + DataGridUser findByDataGridId(long id); + + /** + * Find a list of users by their IDs + * @param ids + * @return List of users + */ + List findByDataGridIdList(String[] ids); + + /** + * Find a user by its username + * @param username + * @return List of users + */ + List findByUsername(String username); + + /** + * Find user by username and zone + * @param username + * @param zone + * @return + */ + DataGridUser findByUsernameAndZone(String username, String zone); + + /** + * Deletes a user by hid id + * @param username + * @param zone + * @return + */ + boolean deleteByDataGridId(long id); + + /** + * Deletes a user by its username + * @param username + * @param zone + * @return + */ + boolean deleteByUsername(String username); + + /** + * Finds users matching the specified query + * @param query + * @return list of users + */ + public List findByQueryString(String query); +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/UserProfileDao.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/UserProfileDao.java new file mode 100755 index 000000000..66f600d9d --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/UserProfileDao.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao; + +import com.emc.metalnx.core.domain.dao.generic.GenericDao; +import com.emc.metalnx.core.domain.entity.UserProfile; + +import java.util.List; + +public interface UserProfileDao extends GenericDao { + + /** + * Returns the list of UserProfiles matching the input string. + * @param query + * @return + */ + List findByQueryString(String query); + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/generic/GenericDao.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/generic/GenericDao.java new file mode 100755 index 000000000..7d7c5e6b7 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/generic/GenericDao.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.generic; + +import org.hibernate.Query; + +import java.io.Serializable; +import java.util.List; + +@SuppressWarnings("rawtypes") +public interface GenericDao { + + /** + * Persists a new entry on the database. + * @param entity + */ + public id save(T entity); + + /** + * Updates an existing entry on the database. + * @param entity + */ + public void merge(T entity); + + /** + * Deletes an entry from the database. + * @param entity + */ + public void delete(T entity); + + /** + * Returns a list of entities of class T matching the query. + * @param query + * @return + */ + public List findMany(Query query); + + /** + * Returns one single entity of class T matching the query. + * @param query + * @return + */ + public T findOne(Query query); + + /** + * Returns all the entries for the class. + * @param clazz + * @return + */ + public List findAll(Class clazz); + + /** + * Find an entry by its ID. + * @param clazz + * @param id + * @return + */ + public T findByID(Class clazz, Long id); +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/generic/GenericDaoImpl.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/generic/GenericDaoImpl.java new file mode 100755 index 000000000..6e50b9309 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/generic/GenericDaoImpl.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.generic; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.Serializable; +import java.util.List; + +@Component +@Transactional +@SuppressWarnings({ "rawtypes", "unchecked" }) +public abstract class GenericDaoImpl implements GenericDao { + + @Resource(name = "sessionFactory") + private SessionFactory sessionFactory; + + protected Session getSession() { + return sessionFactory.getCurrentSession(); + } + + public id save(T entity) { + Session hibernateSession = this.getSession(); + return (id) hibernateSession.save(entity); + } + + public void merge(T entity) { + Session hibernateSession = this.getSession(); + hibernateSession.merge(entity); + } + + public void delete(T entity) { + Session hibernateSession = this.getSession(); + hibernateSession.delete(entity); + } + + public List findMany(Query query) { + List t; + t = (List) query.list(); + return t; + } + + public T findOne(Query query) { + T t; + t = (T) query.uniqueResult(); + return t; + } + + public T findByID(Class clazz, Long id) { + Session hibernateSession = this.getSession(); + T t = null; + t = (T) hibernateSession.get(clazz, id); + return t; + } + + public List findAll(Class clazz) { + Session hibernateSession = this.getSession(); + List T = null; + Query query = hibernateSession.createQuery("from " + clazz.getName()); + T = query.list(); + return T; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/FavoriteDaoImpl.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/FavoriteDaoImpl.java new file mode 100755 index 000000000..70ca0d352 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/FavoriteDaoImpl.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.impl; + +import com.emc.metalnx.core.domain.dao.FavoriteDao; +import com.emc.metalnx.core.domain.dao.UserDao; +import com.emc.metalnx.core.domain.dao.generic.GenericDaoImpl; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserFavorite; +import org.hibernate.Query; +import org.hibernate.SessionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +@Repository +@SuppressWarnings("unchecked") +public class FavoriteDaoImpl extends GenericDaoImpl implements FavoriteDao { + + private static final Logger logger = LoggerFactory.getLogger(FavoriteDaoImpl.class); + + @Autowired + private SessionFactory sessionFactory; + + @Autowired + UserDao userDao; + + @Override + public DataGridUserFavorite findByUserAndPath(DataGridUser user, String path) { + if(user == null || path == null || path.isEmpty()) return null; + + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserFavorite where user_id = :user_id and path = :path"); + q.setLong("user_id", user.getId()); + q.setString("path", path); + return (DataGridUserFavorite) q.uniqueResult(); + } + + @Override + public Long addByUserAndPath(DataGridUser user, String path, boolean isCollection) { + + String parentPath = path.substring(0, path.lastIndexOf("/")); + if (parentPath.isEmpty()) { + parentPath = "/"; + } + String fileName = path != null ? path.substring(path.lastIndexOf("/") + 1, path.length()) : ""; + + DataGridUserFavorite favorite = new DataGridUserFavorite(); + favorite.setUser(user); + favorite.setPath(path); + favorite.setPathHash(path.hashCode()); + favorite.setName(fileName); + favorite.setCreateTs(new Date()); + favorite.setIsCollection(isCollection); + return save(favorite); + } + + @Override + public boolean removeByUserAndPath(DataGridUser user, String path) { + + boolean operationResult = true; + + logger.info("Attempting to remove favorite on {} from user {}", path, user.getUsername()); + try { + DataGridUserFavorite favorite = findByUserAndPath(user, path); + delete(favorite); + logger.info("Successfully removed favorite {} from user{}", path, user.getUsername()); + } + catch (Exception e) { + operationResult = false; + logger.error("Could not remove favorite on {} from user {}: {}", path, user.getUsername(), e.getMessage()); + } + + return operationResult; + } + + @Override + public List findByUser(DataGridUser user) { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserFavorite where user_id = :user_id"); + q.setLong("user_id", user.getId()); + return q.list(); + } + + @Override + public List findByUserPaginated(DataGridUser user, int offset, int limit, String searchString, String orderBy, + String orderDir, boolean onlyCollections) { + String queryString = "from DataGridUserFavorite where user_id = :user_id and path like :path "; + if (onlyCollections) { + queryString += " and is_collection = true "; + } + queryString += " order by " + orderBy + " " + orderDir; + Query q = sessionFactory.getCurrentSession().createQuery(queryString); + q.setLong("user_id", user.getId()); + q.setString("path", '%' + searchString + '%'); + q.setFirstResult(offset); + q.setMaxResults(limit); + return q.list(); + } + + @Override + public List findFavoritesByPath(String path) { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserFavorite where path = :path"); + q.setString("path", path); + return q.list(); + } + + @Override + public boolean removeByPath(String path) { + logger.debug("Removing favorite by path: {} ", path); + boolean removalSuccessful = false; + + try { + List favorites = findFavoritesByPath(path); + Iterator it = favorites.iterator(); + while (it.hasNext()) { + DataGridUserFavorite favorite = it.next(); + logger.debug("Removing favorite {} from database", favorite.getPath()); + delete(favorite); + } + + removalSuccessful = true; + } + catch (Exception e) { + logger.error("Could not remove favorite for path {} ", path); + } + + return removalSuccessful; + } + + @Override + public boolean removeByParentPath(String parentPath) { + logger.debug("Removing favorite by relative path: {} ", parentPath); + boolean removalSuccessful = false; + + try { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserFavorite where path LIKE :path"); + q.setString("path", parentPath + "%"); + + List favorites = q.list(); + + Iterator favoritesIterator = favorites.iterator(); + while (favoritesIterator.hasNext()) { + DataGridUserFavorite currFavorite = favoritesIterator.next(); + logger.debug("Removing relative favorite {} from database", currFavorite.getPath()); + delete(currFavorite); + } + } + catch (Exception e) { + logger.error("Could not relative paths on favorite for path {} ", parentPath); + } + + return removalSuccessful; + } + + @Override + public boolean removeByUser(DataGridUser user) { + logger.debug("Removing favorite by user: {} ", user.getUsername()); + boolean removalSuccessful = false; + + try { + List favorites = findByUser(user); + Iterator it = favorites.iterator(); + while (it.hasNext()) { + DataGridUserFavorite favorite = it.next(); + logger.debug("Removing favorite {} from database", favorite.getPath()); + delete(favorite); + } + + removalSuccessful = true; + } + catch (Exception e) { + logger.error("Could not remove favorite for user {} ", user.getUsername()); + } + + return removalSuccessful; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/GroupBookmarkDaoImpl.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/GroupBookmarkDaoImpl.java new file mode 100755 index 000000000..43e98b853 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/GroupBookmarkDaoImpl.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.impl; + +import com.emc.metalnx.core.domain.dao.GroupBookmarkDao; +import com.emc.metalnx.core.domain.dao.GroupDao; +import com.emc.metalnx.core.domain.dao.generic.GenericDaoImpl; +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridGroupBookmark; +import org.hibernate.Query; +import org.hibernate.SessionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +@Repository +@SuppressWarnings("unchecked") +public class GroupBookmarkDaoImpl extends GenericDaoImpl implements GroupBookmarkDao { + + private static final Logger logger = LoggerFactory.getLogger(GroupBookmarkDaoImpl.class); + + @Autowired + private SessionFactory sessionFactory; + + @Autowired + GroupDao groupDao; + + @Override + public Long addByGroupAndPath(DataGridGroup group, String path, boolean isCollection) { + + String parentPath = path.substring(0, path.lastIndexOf("/")); + if (parentPath.isEmpty()) { + parentPath = "/"; + } + + DataGridGroupBookmark bookmark = new DataGridGroupBookmark(); + bookmark.setGroup(group); + bookmark.setPath(path); + bookmark.setIsCollection(isCollection); + bookmark.setCreateTs(new Date()); + bookmark.setIsNotified(false); + return save(bookmark); + } + + @Override + public boolean removeByGroupAndPath(DataGridGroup group, String path) { + + boolean madeModifications = false; + boolean operationResult = true; + + logger.info("Attempting to remove bookmark on {} from group {}", path, group.getGroupname()); + try { + Iterator it = group.getGroupBookmarks().iterator(); + while (it.hasNext()) { + DataGridGroupBookmark bk = it.next(); + if (bk.getPath().compareTo(path) == 0) { + madeModifications = true; + it.remove(); + } + } + + if (madeModifications) { + logger.debug("Attempting to merge group entity [{}]", group.getId()); + groupDao.merge(group); + logger.info("Successfully removed bookmark {} from group{}", path, group.getGroupname()); + } + } + catch (Exception e) { + operationResult = false; + logger.error("Could not remove bookmark on {} from group {}", path, group.getGroupname(), e); + } + + return operationResult; + } + + @Override + public List findByGroup(DataGridGroup group) { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridGroupBookmark where group_id = :group_id"); + q.setLong("group_id", group.getId()); + return q.list(); + } + + @Override + public List findBookmarksByPath(String path) { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridGroupBookmark where path = :path"); + q.setString("path", path); + return q.list(); + } + + @Override + public boolean removeByPath(String path) { + logger.debug("Removing bookmarks by path: {} ", path); + boolean removalSuccessful = false; + + try { + List bookmarks = findBookmarksByPath(path); + Iterator it = bookmarks.iterator(); + while (it.hasNext()) { + DataGridGroupBookmark bookmark = it.next(); + logger.debug("Removing bookmark {} from database", bookmark.getPath()); + delete(bookmark); + } + + removalSuccessful = true; + } + catch (Exception e) { + logger.error("Could not remove bookmark for path {} ", path); + } + + return removalSuccessful; + } + + @Override + public boolean removeByParentPath(String parentPath) { + logger.debug("Removing bookmarks by relative path: {} ", parentPath); + boolean removalSuccessful = false; + + try { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserBookmark where path LIKE :path"); + q.setString("path", parentPath + "%"); + + List bookmarks = q.list(); + + Iterator bookmarksIterator = bookmarks.iterator(); + while (bookmarksIterator.hasNext()) { + DataGridGroupBookmark currBookmark = bookmarksIterator.next(); + logger.debug("Removing relative bookmark {} from database", currBookmark.getPath()); + delete(currBookmark); + } + } + catch (Exception e) { + logger.error("Could not relative paths on bookmarks for path {} ", parentPath); + } + + return removalSuccessful; + } + + @Override + public boolean removeByGroup(DataGridGroup group) { + boolean operationResult = true; + + List bookmarks = findByGroup(group); + Iterator it = bookmarks.iterator(); + while (it.hasNext()) { + try { + delete(it.next()); + } + catch (Exception e) { + operationResult = false; + } + } + + return operationResult; + } + + @Override + public List findGroupBookmarksByGroupsIds(String[] groupIds, int offset, int limit, String searchString, String orderBy, + String orderDir, boolean onlyCollections) { + if (groupIds != null && groupIds.length > 0) { + Long[] groupIdsLong = convertStringsToLongs(groupIds); + String queryString = "select Dggb from DataGridGroupBookmark Dggb left join Dggb.group Dgg where Dgg.dataGridId in (:groupIds) and (Dggb.path LIKE :path or Dgg.groupname LIKE :groupname) "; + if (onlyCollections) { + queryString += "and Dggb.isCollection = true "; + } + queryString += "order by " + orderBy + " " + orderDir; + Query q = sessionFactory.getCurrentSession().createQuery(queryString); + q.setParameterList("groupIds", groupIdsLong); + q.setString("path", '%' + searchString + '%'); + q.setString("groupname", '%' + searchString + '%'); + q.setFirstResult(offset); + q.setMaxResults(limit); + + return q.list(); + } + + // If the input list is null, the method returns null + return new ArrayList(); + } + + @Override + public Long countGroupBookmarksByGroupsIds(String[] groupIds) { + if (groupIds != null && groupIds.length > 0) { + Long[] groupIdsLong = convertStringsToLongs(groupIds); + Query q = sessionFactory.getCurrentSession().createQuery( + "select count(*) from DataGridGroupBookmark Dggb left join Dggb.group Dgg where Dgg.dataGridId in (:groupIds)"); + q.setParameterList("groupIds", groupIdsLong); + + return (Long) q.uniqueResult(); + } + return (long) 0; + } + + @Override + public boolean updateBookmark(String oldPath, String newPath) { + logger.debug("Updating bookmark"); + + if (oldPath == null || newPath == null) { + logger.debug("Could not update bookmark. Null values provided"); + return false; + } + + if (oldPath.equals(newPath)) { + logger.debug("Old bookmark is the same as the new one. No need for an update."); + return false; + } + + Query q = sessionFactory.getCurrentSession().createQuery("update DataGridGroupBookmark set path = :newPath where path = :oldPath"); + q.setString("newPath", newPath); + q.setString("oldPath", oldPath); + + return q.executeUpdate() > 0; + } + + /** + * Converts an array of strings into an array of longs. + * + * @param strArray + * array of strings to be converted + * @return array of longs + */ + private Long[] convertStringsToLongs(String[] strArray) { + Long[] intArray = new Long[strArray.length]; + + for (int i = 0; i < strArray.length; i++) { + intArray[i] = Long.valueOf(strArray[i]); + } + + return intArray; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/GroupDaoImpl.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/GroupDaoImpl.java new file mode 100755 index 000000000..5d4ca0b41 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/GroupDaoImpl.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.impl; + +import com.emc.metalnx.core.domain.dao.GroupDao; +import com.emc.metalnx.core.domain.dao.generic.GenericDaoImpl; +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import org.hibernate.Query; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("unchecked") +@Repository +public class GroupDaoImpl extends GenericDaoImpl implements GroupDao { + + @Autowired + private SessionFactory sessionFactory; + + @Override + public List findByGroupname(String groupname) { + + List dataGridGroups = null; + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridGroup where groupname = :groupname"); + q.setString("groupname", groupname); + + dataGridGroups = q.list(); + + return dataGridGroups; + } + + @Override + public DataGridGroup findByGroupnameAndZone(String groupname, String zone) { + + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridGroup where groupname = :groupname and additional_info = :zone"); + q.setString("groupname", groupname); + q.setParameter("zone", zone); + + return (DataGridGroup) q.uniqueResult(); + } + + @Override + public boolean deleteByGroupname(String groupname) { + + boolean operationResult = true; + + try { + List dataGridGroups = findByGroupname(groupname); + + for (DataGridGroup dataGridGroup : dataGridGroups) { + delete(dataGridGroup); + } + } + catch (Exception e) { + operationResult = false; + } + + return operationResult; + } + + @Override + public List findByQueryString(String query) { + Query q = sessionFactory.getCurrentSession().createQuery( + "from DataGridGroup where groupname like :groupname or additional_info like :additional_info"); + + q.setParameter("groupname", "%" + query + "%"); + q.setParameter("additional_info", "%" + query + "%"); + + // Returning results + return q.list(); + } + + @Override + public List findByDataGridIdList(String[] ids) { + // Checking if the input ID list is empty + if (ids == null || ids.length == 0) { + return new ArrayList(); + } + + Long[] idsLong = convertStringsToLongs(ids); + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridGroup where data_grid_id in (:ids)"); + q.setParameterList("ids", idsLong); + + return q.list(); + + } + + @Override + public List findByGroupNameList(String[] groupNames) { + // Checking if the input ID list is empty + if (groupNames == null || groupNames.length == 0) { + return new ArrayList(); + } + + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridGroup where groupname in (:groupNames)"); + q.setParameterList("groupNames", groupNames); + + return q.list(); + } + + @Override + public boolean deleteByDataGridGroupId(long id) { + DataGridGroup dataGridGroup = findByDataGridId(id); + + if (dataGridGroup != null) { + delete(dataGridGroup); + return true; + } + + return false; + } + + @Override + public DataGridGroup findByDataGridId(long id) { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridGroup where data_grid_id=(:id)"); + q.setParameter("id", id); + + List groups = q.list(); + return groups.size() > 0 ? groups.get(0) : null; + } + + @Override + public List findByIdList(Long[] ids) { + if (ids == null || ids.length == 0) { + return new ArrayList(); + } + + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridGroup where id in (:ids)"); + q.setParameterList("ids", ids); + + return q.list(); + } + + /** + * Converts an array of strings into an array of longs. + * + * @param strArray + * array of strings to be converted + * @return array of longs + */ + private Long[] convertStringsToLongs(String[] strArray) { + Long[] intArray = new Long[strArray.length]; + + for (int i = 0; i < strArray.length; i++) { + intArray[i] = Long.valueOf(strArray[i]); + } + + return intArray; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/TemplateDaoImpl.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/TemplateDaoImpl.java new file mode 100755 index 000000000..08f9c397a --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/TemplateDaoImpl.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.impl; + +import com.emc.metalnx.core.domain.dao.TemplateDao; +import com.emc.metalnx.core.domain.dao.generic.GenericDaoImpl; +import com.emc.metalnx.core.domain.entity.DataGridTemplate; +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; +import org.hibernate.Query; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("unchecked") +@Repository +public class TemplateDaoImpl extends GenericDaoImplimplements TemplateDao { + + @Autowired + private SessionFactory sessionFactory; + + @Override + public long getTemplateId(String templateName) { + DataGridTemplate dataGridTemplate = this.findByName(templateName); + + if (dataGridTemplate == null) { + return -1; + } + + return dataGridTemplate.getId(); + } + + @Override + public DataGridTemplate findById(long id) { + + Query q = this.sessionFactory.getCurrentSession().createQuery("from DataGridTemplate where template_id=(:id)"); + q.setParameter("id", id); + + return (DataGridTemplate) q.uniqueResult(); + } + + @Override + public DataGridTemplate findByName(String templateName) { + + Query q = this.sessionFactory.getCurrentSession() + .createQuery("from DataGridTemplate where template_name = :templateName"); + q.setString("templateName", templateName); + + return (DataGridTemplate) q.uniqueResult(); + } + + @Override + public boolean deleteById(long id) { + DataGridTemplate dataGridTemplate = this.findById(id); + + if (dataGridTemplate == null) { + return false; + } + + this.delete(dataGridTemplate); + + return true; + } + + @Override + public List findByQueryString(String query) { + Query q = this.sessionFactory.getCurrentSession() + .createQuery("from DataGridTemplate where template_name like :templateName"); + + q.setParameter("templateName", "%" + query + "%"); + + // Returning results + return q.list(); + } + + @Override + public List listTemplateFields(Long id) { + Query q = this.sessionFactory.getCurrentSession() + .createQuery("from DataGridTemplateField where template_id = :templateID"); + + q.setParameter("templateID", id); + + // Returning results + return q.list(); + } + + @Override + public List listTemplateFields(String template) { + long id = this.getTemplateId(template); + + if (id > 0) { + return this.listTemplateFields(id); + } + + return new ArrayList(); + } + + @Override + public List listPublicTemplates() { + Query q = this.sessionFactory.getCurrentSession() + .createQuery("from DataGridTemplate where access_type = :accessType"); + + q.setParameter("accessType", "system"); + + // Returning results + return q.list(); + } + + @Override + public List listPrivateTemplatesByUser(String user) { + Query q = this.sessionFactory.getCurrentSession() + .createQuery("from DataGridTemplate where access_type = :accessType and owner = :owner"); + + q.setParameter("accessType", "private"); + q.setParameter("owner", user); + + // Returning results + return q.list(); + } + + @Override + public void merge(DataGridTemplate template) { + if (template.isModified()) { + template.setVersion(template.getVersion() + 1); + } + + super.merge(template); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/TemplateFieldsDaoImpl.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/TemplateFieldsDaoImpl.java new file mode 100755 index 000000000..daf685e02 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/TemplateFieldsDaoImpl.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.impl; + +import com.emc.metalnx.core.domain.dao.TemplateFieldDao; +import com.emc.metalnx.core.domain.dao.generic.GenericDaoImpl; +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateAttrException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateUnitException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateValueException; +import org.hibernate.Query; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +@SuppressWarnings("unchecked") +@Repository +public class TemplateFieldsDaoImpl extends GenericDaoImpl implements TemplateFieldDao { + + @Autowired + private SessionFactory sessionFactory; + + @Override + public DataGridTemplateField findById(long id) { + + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridTemplateField where template_field_id=(:id)"); + q.setParameter("id", id); + + return (DataGridTemplateField) q.uniqueResult(); + } + + @Override + public boolean modifyById(long id, String attribute, String value, String unit) throws DataGridTemplateAttrException, + DataGridTemplateValueException, DataGridTemplateUnitException { + DataGridTemplateField templateField = findById(id); + + if (templateField == null) { + return false; + } + + templateField.setAttribute(attribute); + templateField.setValue(value); + templateField.setUnit(unit); + merge(templateField); + + return true; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/UserBookmarkDaoImpl.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/UserBookmarkDaoImpl.java new file mode 100755 index 000000000..c1aa6e0f9 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/UserBookmarkDaoImpl.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.impl; + +import com.emc.metalnx.core.domain.dao.UserBookmarkDao; +import com.emc.metalnx.core.domain.dao.UserDao; +import com.emc.metalnx.core.domain.dao.generic.GenericDaoImpl; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserBookmark; +import org.hibernate.Query; +import org.hibernate.SessionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +@Repository +@SuppressWarnings("unchecked") +public class UserBookmarkDaoImpl extends GenericDaoImpl implements UserBookmarkDao { + + private static final Logger logger = LoggerFactory.getLogger(UserBookmarkDaoImpl.class); + + @Autowired + private SessionFactory sessionFactory; + + @Autowired + UserDao userDao; + + @Override + public DataGridUserBookmark findByUserAndPath(DataGridUser user, String path) { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserBookmark where user_id = :user_id and path = :path"); + q.setLong("user_id", user.getId()); + q.setString("path", path); + return (DataGridUserBookmark) q.uniqueResult(); + } + + @Override + public Long addByUserAndPath(DataGridUser user, String path, boolean isCollection) { + + String parentPath = path.substring(0, path.lastIndexOf("/")); + if (parentPath.isEmpty()) { + parentPath = "/"; + } + + String fileName = path != null ? path : ""; + fileName = fileName.substring(fileName.lastIndexOf("/") + 1, fileName.length()); + + DataGridUserBookmark bookmark = new DataGridUserBookmark(); + bookmark.setUser(user); + bookmark.setPath(path); + bookmark.setName(fileName); + bookmark.setCreateTs(new Date()); + bookmark.setIsNotified(false); + bookmark.setIsCollection(isCollection); + return save(bookmark); + } + + @Override + public boolean removeByUserAndPath(DataGridUser user, String path) { + + boolean operationResult = true; + + logger.info("Attempting to remove bookmark on {} from user {}", path, user.getUsername()); + try { + DataGridUserBookmark bookmark = findByUserAndPath(user, path); + delete(bookmark); + logger.info("Successfully removed bookmark {} from user{}", path, user.getUsername()); + } + catch (Exception e) { + operationResult = false; + logger.error("Could not remove bookmark on {} from user {}: {}", path, user.getUsername(), e.getMessage()); + } + + return operationResult; + } + + @Override + public List findByUser(DataGridUser user) { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserBookmark where user_id = :user_id"); + q.setLong("user_id", user.getId()); + return q.list(); + } + + @Override + public List findBookmarksByPath(String path) { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserBookmark where path = :path"); + q.setString("path", path); + return q.list(); + } + + @Override + public boolean removeByPath(String path) { + logger.debug("Removing bookmarks by path: {} ", path); + boolean removalSuccessful = false; + + try { + List bookmarks = findBookmarksByPath(path); + Iterator it = bookmarks.iterator(); + while (it.hasNext()) { + DataGridUserBookmark bookmark = it.next(); + logger.debug("Removing bookmark {} from database", bookmark.getPath()); + delete(bookmark); + } + + removalSuccessful = true; + } + catch (Exception e) { + logger.error("Could not remove bookmark for path {} ", path); + } + + return removalSuccessful; + } + + @Override + public boolean removeByParentPath(String parentPath) { + logger.debug("Removing bookmarks by relative path: {} ", parentPath); + boolean removalSuccessful = false; + + try { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserBookmark where path LIKE :path"); + q.setString("path", parentPath + "%"); + + List bookmarks = q.list(); + + Iterator bookmarksIterator = bookmarks.iterator(); + while (bookmarksIterator.hasNext()) { + DataGridUserBookmark currBookmark = bookmarksIterator.next(); + logger.debug("Removing relative bookmark {} from database", currBookmark.getPath()); + delete(currBookmark); + } + } + catch (Exception e) { + logger.error("Could not relative paths on bookmarks for path {} ", parentPath); + } + + return removalSuccessful; + } + + @Override + public List findByUserPaginated(DataGridUser user, int start, int length, String searchString, String orderBy, + String orderDir, boolean onlyCollections) { + List orderByList = new ArrayList(); + orderByList.add(orderBy); + + List orderDirList = new ArrayList(); + orderDirList.add(orderDir); + + return this.findByUserPaginated(user, start, length, searchString, orderByList, orderDirList, onlyCollections); + } + + @Override + public List findByUserPaginated(DataGridUser user, int start, int length, String searchString, List orderBy, + List orderDir, boolean onlyCollections) { + + if (orderBy.size() != orderDir.size()) { + return null; + } + + StringBuilder query = new StringBuilder(); + query.append("from DataGridUserBookmark "); + query.append("where "); + query.append("user_id = :user_id "); + query.append("and "); + query.append("path like :path "); + if (onlyCollections) { + query.append("and is_collection = true "); + } + query.append("order by "); + + for (int i = 0; i < orderBy.size(); i++) { + query.append(orderBy.get(i)); + query.append(" "); + query.append(orderDir.get(i)); + query.append(" "); + if (i + 1 < orderBy.size()) { + query.append(","); + } + } + + Query q = sessionFactory.getCurrentSession().createQuery(query.toString()); + q.setLong("user_id", user.getId()); + q.setString("path", '%' + searchString + '%'); + q.setFirstResult(start); + q.setMaxResults(length); + return q.list(); + } + + @Override + public boolean removeByUser(DataGridUser user) { + + boolean operationResult = true; + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUserBookmark where user_id = :user_id"); + q.setLong("user_id", user.getId()); + List bookmarks = q.list(); + Iterator it = bookmarks.iterator(); + + try { + while (it.hasNext()) { + delete(it.next()); + } + } + catch (Exception e) { + operationResult = false; + } + + return operationResult; + } + + @Override + public boolean updateBookmark(String oldPath, String newPath) { + logger.debug("Updating bookmark"); + + if (oldPath == null || newPath == null) { + logger.debug("Could not update bookmark. Null values provided"); + return false; + } + + if (oldPath.equals(newPath)) { + logger.debug("Old bookmark is the same as the new one. No need for an update."); + return false; + } + + String newBookmarkName = newPath.substring(newPath.lastIndexOf("/") + 1, newPath.length()); + + Query q = sessionFactory.getCurrentSession().createQuery("update DataGridUserBookmark set path = :newPath, name = :name where path = :oldPath"); + q.setString("newPath", newPath); + q.setString("name", newBookmarkName); + q.setString("oldPath", oldPath); + return q.executeUpdate() > 0; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/UserDaoImpl.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/UserDaoImpl.java new file mode 100755 index 000000000..a82ab88b3 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/UserDaoImpl.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.impl; + +import com.emc.metalnx.core.domain.dao.UserDao; +import com.emc.metalnx.core.domain.dao.generic.GenericDaoImpl; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import org.hibernate.Query; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("unchecked") +@Repository +@Transactional +public class UserDaoImpl extends GenericDaoImpl implements UserDao { + + @Autowired + private SessionFactory sessionFactory; + + @Override + public List findByUsername(String username) { + + List users = null; + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUser where username = :username"); + q.setString("username", username); + + users = q.list(); + + return users; + } + + @Override + public DataGridUser findByUsernameAndZone(String username, String zone) { + + List users = null; + + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUser where username = :username and additional_info = :zone"); + q.setString("username", username); + q.setString("zone", zone); + + users = q.list(); + return users.size() > 0 ? users.get(0) : null; + } + + @Override + public boolean deleteByUsername(String username) { + List users = findByUsername(username); + for (DataGridUser user : users) { + delete(user); + } + return true; + } + + @Override + public List findByQueryString(String query) { + Query q = sessionFactory.getCurrentSession().createQuery( + "from DataGridUser where username like :username or additional_info like :additional_info " + + "or first_name like :first_name or last_name like :last_name " + "or email like :email order by username"); + + q.setParameter("username", "%" + query + "%"); + q.setParameter("additional_info", "%" + query + "%"); + q.setParameter("first_name", "%" + query + "%"); + q.setParameter("last_name", "%" + query + "%"); + q.setParameter("email", "%" + query + "%"); + + // Returning results + return q.list(); + } + + @Override + public List findByDataGridIdList(String[] ids) { + List result = new ArrayList(); + + if (ids != null) { + int i = 0; + Integer ids_int[] = new Integer[ids.length]; + + for (String id_str : ids) { + ids_int[i++] = Integer.parseInt(id_str); + } + + // Checking if the input ID list is empty + if (ids_int != null) { + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUser where data_grid_id in (:ids)"); + q.setParameterList("ids", ids_int); + result = q.list(); + } + } + + // If the input list is null, the method returns null + return result; + } + + @Override + public DataGridUser findByDataGridId(long id) { + + Query q = sessionFactory.getCurrentSession().createQuery("from DataGridUser where data_grid_id=(:id)"); + q.setParameter("id", id); + + List users = q.list(); + + return users.size() > 0 ? users.get(0) : null; + } + + @Override + public boolean deleteByDataGridId(long id) { + DataGridUser user = findByDataGridId(id); + if (user != null) { + delete(user); + } + return false; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/UserProfileDaoImpl.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/UserProfileDaoImpl.java new file mode 100755 index 000000000..2c53ecdab --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/dao/impl/UserProfileDaoImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.dao.impl; + +import com.emc.metalnx.core.domain.dao.UserProfileDao; +import com.emc.metalnx.core.domain.dao.generic.GenericDaoImpl; +import com.emc.metalnx.core.domain.entity.UserProfile; +import org.hibernate.Query; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@SuppressWarnings("unchecked") +@Repository +@Transactional +public class UserProfileDaoImpl extends GenericDaoImpl + implements UserProfileDao { + + @Autowired + private SessionFactory sessionFactory; + + @Override + public List findByQueryString(String query) { + Query q = sessionFactory + .getCurrentSession() + .createQuery( + "from UserProfile where profile_name like :name or description like :description "); + + q.setParameter("name", "%" + query + "%"); + q.setParameter("description", "%" + query + "%"); + + // Returning results + return q.list(); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridCollectionAndDataObject.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridCollectionAndDataObject.java new file mode 100755 index 000000000..e0989b8f0 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridCollectionAndDataObject.java @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +public class DataGridCollectionAndDataObject implements Serializable { + + private static final String DATE_FORMAT_STR = "MMM dd yyyy HH:mm"; + + private String name; + private String path; + private String parentPath; + private String owner; + private Date modifiedAt; + private Date createdAt; + private boolean isCollection; + private String checksum; + private String replicaNumber; + private int numberOfReplicas; + private String displaySize; + private String mostPermissiveAccessForCurrentUser; + private long size; + private String resourceName; + private boolean inheritanceOption; + private boolean visibleToCurrentUser; + /** + * Indicates that this collection is actually a proxy to a collection the user + * cannot see in iRODS, but is necessary in order to drill down when StrictACLs + * are active + */ + private boolean isProxy = false; + + // number of matches on a metadata search + private int numberOfMatches; + + private static final long serialVersionUID = 1L; + + public DataGridCollectionAndDataObject() { + + } + + public DataGridCollectionAndDataObject(String path, String parentPath, boolean isCollection) { + this.path = path; + this.parentPath = parentPath; + this.name = findCollectionName(path); + this.isCollection = isCollection; + } + + public DataGridCollectionAndDataObject(String path, String name, String parentPath, boolean isCollection) { + this.path = path; + this.name = name; + this.parentPath = parentPath; + this.isCollection = isCollection; + } + + /** + * Finds a collection name based on its path + * + * @param collectionPath + * @return + */ + public String findCollectionName(String collectionPath) { + String[] pathParts = collectionPath.split("/"); + return pathParts[pathParts.length - 1]; + } + + /** + * @return the collectionName + */ + public String getName() { + return name; + } + + /** + * @param name + * the collectionName to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the collectionPath + */ + public String getPath() { + return path; + } + + /** + * @param path + * the collectionPath to set + */ + public void setPath(String path) { + this.path = path; + } + + /** + * @return the parentCollectionPath + */ + public String getParentPath() { + return parentPath; + } + + /** + * @param parentPath + * the parentCollectionPath to set + */ + public void setParentPath(String parentPath) { + this.parentPath = parentPath; + } + + /** + * @return the isCollection + */ + public boolean isCollection() { + return isCollection; + } + + /** + * Checks if current object corresponds data object in the grid. + * + * @return True, if current object is a data object in the grid. False, + * otherwise. + */ + public boolean isDataObject() { + return !isCollection; + } + + /** + * @param isCollection + * the isCollection to set + */ + public void setCollection(boolean isCollection) { + this.isCollection = isCollection; + } + + /** + * Gets the icon type that will be shown on the UI. + * + * @return the icon type as String + */ + public String getIconToDisplay() { + + if (isCollection) + return "fa fa-folder"; + + String icon = ""; + String extension = this.getFileExtension(this.getName()); + + switch (extension) { + + case "jpg": + case "jpeg": + case "png": + case "gif": + icon = "fa fa-file-image-o"; + break; + + case "html": + case "htm": + case "xml": + case "tex": + icon = "fa fa-code"; + break; + + case "c": + case "cpp": + case "java": + case "py": + icon = "fa fa-file-code-o"; + break; + + case "docx": + case "doc": + icon = "fa fa-file-word-o"; + break; + + case "xlsx": + case "xls": + icon = "fa fa-file-excel-o"; + break; + + case "pptx": + case "ppt": + icon = "fa fa-file-powerpoint-o"; + break; + + case "pdf": + icon = "fa fa-file-pdf-o"; + break; + + case "zip": + case "rar": + icon = "fa fa-file-archive-o"; + break; + + default: + icon = "fa fa-file"; + break; + } + + return icon; + + } + + private String getFileExtension(String fileName) { + + int lastIndexOfFileName = fileName.lastIndexOf("."); + if (lastIndexOfFileName != -1 && lastIndexOfFileName != 0) { + return fileName.substring(lastIndexOfFileName + 1); + } + + else + return "unknown"; + + } + + /** + * @return the modified + */ + public Date getModifiedAt() { + return modifiedAt; + } + + /** + * @param modifiedAt + * the modified to set + */ + public void setModifiedAt(Date modifiedAt) { + this.modifiedAt = modifiedAt; + } + + /** + * @return the owner + */ + public String getOwner() { + return owner; + } + + /** + * @param owner + * the owner to set + */ + public void setOwner(String owner) { + this.owner = owner; + } + + /** + * @return the created + */ + public Date getCreatedAt() { + return createdAt; + } + + /** + * @param createdAt + * the created to set + */ + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + + /** + * Formats the date when a collection/data object was modified + * + * @return String in the format MM/DD/YYYY HH:MM + */ + public String getModifiedAtFormatted() { + if (modifiedAt == null) + return ""; + return new SimpleDateFormat(DATE_FORMAT_STR).format(modifiedAt); + } + + /** + * Formats the date when a collection/data object was modified for the CSV + * report + * + * @return String in the format MM/DD/YYYY HH:MM + */ + public String getModifiedAtFormattedForCSVReport() { + return new SimpleDateFormat("MMM-dd-yyyy_HH-mm").format(modifiedAt); + } + + /** + * Formats the date when a collection/data object was created + * + * @return the modified + */ + @JsonIgnore + public String getCreatedAtFormatted() { + if (createdAt == null) + return ""; + return new SimpleDateFormat(DATE_FORMAT_STR).format(createdAt); + } + + /** + * @return the checksum + */ + public String getChecksum() { + return checksum; + } + + /** + * @param checksum + * the checksum to set + */ + public void setChecksum(String checksum) { + this.checksum = checksum; + } + + /** + * @return the replicaNumber + */ + public String getReplicaNumber() { + return replicaNumber; + } + + /** + * @param replicaNumber + * the replicaNumber to set + */ + public void setReplicaNumber(String replicaNumber) { + this.replicaNumber = replicaNumber; + } + + /** + * @return the numerOfReplicas + */ + public int getNumberOfReplicas() { + return numberOfReplicas; + } + + /** + * @param numerOfReplicas + * the numerOfReplicas to set + */ + public void setNumberOfReplicas(int numerOfReplicas) { + this.numberOfReplicas = numerOfReplicas; + } + + /** + * @return the displaySize + */ + public String getDisplaySize() { + return displaySize; + } + + /** + * @param displaySize + * the displaySize to set + */ + public void setDisplaySize(String displaySize) { + this.displaySize = displaySize; + } + + /** + * @return the size + */ + public long getSize() { + return size; + } + + /** + * @param size + * the size to set + */ + public void setSize(long size) { + this.size = size; + } + + /** + * @return the resourceName + */ + public String getResourceName() { + return resourceName; + } + + /** + * @param resourceName + * the resourceName to set + */ + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + /** + * @return the inheritance option + */ + public boolean isInheritanceOption() { + return inheritanceOption; + } + + /** + * @param inheritanceOption + * the inheritance option to set + */ + public void setInheritanceOption(boolean inheritanceOption) { + this.inheritanceOption = inheritanceOption; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("DataGridCollectionAndDataObject ["); + if (name != null) { + builder.append("name=").append(name).append(", "); + } + if (path != null) { + builder.append("path=").append(path).append(", "); + } + if (parentPath != null) { + builder.append("parentPath=").append(parentPath).append(", "); + } + if (owner != null) { + builder.append("owner=").append(owner).append(", "); + } + if (modifiedAt != null) { + builder.append("modifiedAt=").append(modifiedAt).append(", "); + } + if (createdAt != null) { + builder.append("createdAt=").append(createdAt).append(", "); + } + builder.append("isCollection=").append(isCollection).append(", "); + if (checksum != null) { + builder.append("checksum=").append(checksum).append(", "); + } + if (replicaNumber != null) { + builder.append("replicaNumber=").append(replicaNumber).append(", "); + } + builder.append("numberOfReplicas=").append(numberOfReplicas).append(", "); + if (displaySize != null) { + builder.append("displaySize=").append(displaySize).append(", "); + } + if (mostPermissiveAccessForCurrentUser != null) { + builder.append("mostPermissiveAccessForCurrentUser=").append(mostPermissiveAccessForCurrentUser) + .append(", "); + } + builder.append("size=").append(size).append(", "); + if (resourceName != null) { + builder.append("resourceName=").append(resourceName).append(", "); + } + builder.append("inheritanceOption=").append(inheritanceOption).append(", visibleToCurrentUser=") + .append(visibleToCurrentUser).append(", isProxy=").append(isProxy).append(", numberOfMatches=") + .append(numberOfMatches).append("]"); + return builder.toString(); + } + + /** + * @return the numberOfMatches + */ + public int getNumberOfMatches() { + return numberOfMatches; + } + + /** + * @param numberOfMatches + * the numberOfMatches to set + */ + public void setNumberOfMatches(int numberOfMatches) { + this.numberOfMatches = numberOfMatches; + } + + /** + * @return the visibleToCurrentUser + */ + public boolean isVisibleToCurrentUser() { + return visibleToCurrentUser; + } + + /** + * @param visibleToCurrentUser + * the visibleToCurrentUser to set + */ + public void setVisibleToCurrentUser(boolean visibleToCurrentUser) { + this.visibleToCurrentUser = visibleToCurrentUser; + } + + public String getMostPermissiveAccessForCurrentUser() { + return mostPermissiveAccessForCurrentUser; + } + + public void setMostPermissiveAccessForCurrentUser(String mostPermissiveAccessForCurrentUser) { + this.mostPermissiveAccessForCurrentUser = mostPermissiveAccessForCurrentUser; + } + + public boolean isProxy() { + return isProxy; + } + + public void setProxy(boolean isProxy) { + this.isProxy = isProxy; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridFilePermission.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridFilePermission.java new file mode 100755 index 000000000..842d574c1 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridFilePermission.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import java.io.Serializable; + +public class DataGridFilePermission implements Serializable { + private String userId; + private String username; + private String permission; + private String userType; + private String userZone; + private static final long serialVersionUID = 1L; + + public DataGridFilePermission() { + + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserZone() { + return userZone; + } + + public void setUserZone(String userZone) { + this.userZone = userZone; + } + + public String getUserType() { + return userType; + } + + public void setUserType(String userType) { + this.userType = userType; + } + +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridFilePropertySearch.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridFilePropertySearch.java new file mode 100755 index 000000000..b0b3d32d1 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridFilePropertySearch.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +import com.emc.metalnx.core.domain.entity.enums.DataGridSearchOperatorEnum; +import com.emc.metalnx.core.domain.entity.enums.FilePropertyField; + +public class DataGridFilePropertySearch { + + private final FilePropertyField attribute; + private final DataGridSearchOperatorEnum operator; + private final String value; + + public DataGridFilePropertySearch(FilePropertyField filePropertyField, DataGridSearchOperatorEnum operator, + String value) throws ParseException { + attribute = filePropertyField; + this.operator = operator; + if (filePropertyField == FilePropertyField.CREATION_DATE + || filePropertyField == FilePropertyField.MODIFICATION_DATE) { + long timeInMilliseconds = new SimpleDateFormat("MM/dd/yyyy hh:mm aa").parse(value).getTime(); + this.value = String.valueOf(timeInMilliseconds / 1000); + } else { + this.value = value; + } + } + + @Override + public String toString() { + return String.format("%s %s %s", attribute.getFieldName(), operator.toString(), value); + } + + /** + * @return the attribute + */ + public FilePropertyField getAttribute() { + return attribute; + } + + /** + * @return the operator + */ + public DataGridSearchOperatorEnum getOperator() { + return operator; + } + + /** + * @return the value + */ + public String getValue() { + return value; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridGroup.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridGroup.java new file mode 100755 index 000000000..8e3d92bae --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridGroup.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Set; + +@Entity +@Audited +@Table(name = "groups") +public class DataGridGroup implements Serializable, Comparable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @NotAudited + @Column(name = "id", unique = true, nullable = false) + private Long id; + + @Column(name = "data_grid_id", unique = true, nullable = false) + private long dataGridId; + + @Column(name = "groupname", unique = true, nullable = false, length = 60) + private String groupname; + + @Column(name = "additional_info", nullable = true, length = 60) + private String additional_info; + + @ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST }) + @JoinTable(name = "user_profile_groups", joinColumns = { @JoinColumn(name = "group_id") }, inverseJoinColumns = { @JoinColumn(name = "profile_id") }) + private Set userProfiles; + + @JsonIgnore + @OneToMany(mappedBy = "group", fetch = FetchType.EAGER, cascade = CascadeType.DETACH, orphanRemoval = true) + private Set groupBookmarks; + + public DataGridGroup() { + + } + + public DataGridGroup(String groupname, String additional_info) { + this.groupname = groupname; + this.additional_info = additional_info; + } + + public String getDisplayName() { + + return groupname; + } + + /** + * @return the id + */ + public long getId() { + return id; + } + + /** + * @return the dataGridId + */ + public long getDataGridId() { + return dataGridId; + } + + /** + * @param id + * the id to set + */ + public void setId(long id) { + this.id = id; + } + + /** + * @param dataGridId + * the dataGridId to set + */ + public void setDataGridId(long dataGridId) { + this.dataGridId = dataGridId; + } + + /** + * @return the groupName + */ + public String getGroupname() { + return groupname; + } + + /** + * @param the + * groupName to set + */ + public void setGroupname(String groupName) { + groupname = groupName; + } + + /** + * @return group's zone + */ + public String getAdditionalInfo() { + return additional_info; + } + + /** + * @param the + * zone to set + */ + public void setAdditionalInfo(String additional_info) { + this.additional_info = additional_info; + } + + /** + * @return the groupBookmarks + */ + public Set getGroupBookmarks() { + return groupBookmarks; + } + + /** + * @param groupBookmarks + * the groupBookmarks to set + */ + public void setGroupBookmarks(Set groupBookmarks) { + this.groupBookmarks = groupBookmarks; + } + + @Override + public int compareTo(DataGridGroup dgg) { + return groupname.compareTo(dgg.getGroupname()); + } + + @Override + public String toString() { + return groupname; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridGroupBookmark.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridGroupBookmark.java new file mode 100755 index 000000000..a8451a848 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridGroupBookmark.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.utils.DataGridCoreUtils; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +import javax.persistence.*; +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.Date; + +@Entity +@Audited +@Table(name = "group_bookmarks") +public class DataGridGroupBookmark implements Serializable, Comparable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @NotAudited + @Column(name = "id", unique = true, nullable = false) + private Long id; + + @ManyToOne(fetch = FetchType.EAGER, optional = true) + @JoinColumn(name = "group_id", nullable = false, updatable = true) + private DataGridGroup group; + + @Column(name = "path", nullable = false, length = 512) + private String path; + + @Column(name = "is_notified", nullable = true) + private Boolean isNotified; + + @Column(name = "is_collection", nullable = true) + private Boolean isCollection; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_at", nullable = false, length = 60, updatable = false) + private Date createTs; + + private static final long serialVersionUID = -229875209906357557L; + + /** + * @return the id + */ + public Long getId() { + return id; + } + + /** + * @return the group + */ + public DataGridGroup getGroup() { + return group; + } + + /** + * @return the path + */ + public String getPath() { + return path; + } + + /** + * @return the isNotified + */ + public Boolean getIsNotified() { + return isNotified; + } + + /** + * @return the createTs + */ + public Date getCreateTs() { + return createTs; + } + + /** + * @param id + * the id to set + */ + public void setId(Long id) { + this.id = id; + } + + /** + * @param group + * the group to set + */ + public void setGroup(DataGridGroup group) { + this.group = group; + } + + /** + * @param path + * the path to set + */ + public void setPath(String path) { + this.path = path; + } + + /** + * @param isNotified + * the isNotified to set + */ + public void setIsNotified(Boolean isNotified) { + this.isNotified = isNotified; + } + + /** + * @return the isCollection + */ + public Boolean getIsCollection() { + return isCollection; + } + + /** + * @param isCollection + * the isCollection to set + */ + public void setIsCollection(Boolean isCollection) { + this.isCollection = isCollection; + } + + /** + * @param createTs + * the createTs to set + */ + public void setCreateTs(Date createTs) { + this.createTs = createTs; + } + + /** + * Finds the file name based on its path + * + * @return file name + */ + public String getFileName() { + if (getPath() == null) { + return new String(); + } + + String fileName = getPath() != null ? getPath() : ""; + fileName = fileName.substring(fileName.lastIndexOf("/") + 1, fileName.length()); + return fileName; + } + + /** + * Gets the icon to be displayed for a bookmarks based on its extension + * + * @return String containing the icon name to be displayed + */ + public String getDisplayIcon() { + return DataGridCoreUtils.getIconToDisplay(getPath()); + } + + /** + * Formats the date when a collection/data object was modified + * + * @return String in the format MM/DD/YYYY HH:MM + */ + public String getCreatedAtFormatted() { + return new SimpleDateFormat("MMM dd yyyy, HH:mm").format(createTs); + } + + @Override + public int compareTo(DataGridGroupBookmark dgub) { + return getFileName().compareTo(dgub.getFileName()); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridGroupPermission.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridGroupPermission.java new file mode 100755 index 000000000..74512bde3 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridGroupPermission.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +public class DataGridGroupPermission { + + private Integer dataGridId; + private String groupName; + private String permission; + + /** + * @return the dataGridId + */ + public Integer getDataGridId() { + return dataGridId; + } + + /** + * @return the groupName + */ + public String getGroupName() { + return groupName; + } + + /** + * @return the permission + */ + public String getPermission() { + return permission; + } + + /** + * @param dataGridId + * the dataGridId to set + */ + public void setDataGridId(Integer dataGridId) { + this.dataGridId = dataGridId; + } + + /** + * @param groupName + * the groupName to set + */ + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + /** + * @param permission + * the permission to set + */ + public void setPermission(String permission) { + this.permission = permission; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMSIPkgInfo.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMSIPkgInfo.java new file mode 100755 index 000000000..6d523fc41 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMSIPkgInfo.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.utils.DataGridCoreUtils; + +import java.util.List; + +/** + * Represents the status of the MSI package on the grid. + */ +public class DataGridMSIPkgInfo { + private List servers; + private boolean isThereAnyPkgMissing; // MSI pkg is not installed in one or more servers + private boolean isThereAnyPkgNotSupported; // MSI Pkg is installed but out-of-date + + public DataGridMSIPkgInfo(List servers, String msiVersionSupported) { + this.servers = servers; + String versionSupported = DataGridCoreUtils.getAPIVersion(msiVersionSupported); + + for(DataGridServer server: servers) { + String versionInstalled = DataGridCoreUtils.getAPIVersion(server.getMSIVersion()); + + if(versionInstalled.isEmpty()) isThereAnyPkgMissing = true; + else if(!versionInstalled.equalsIgnoreCase(versionSupported)) isThereAnyPkgNotSupported = true; + } + } + + public List getServers() { + return servers; + } + + public void setServers(List servers) { + this.servers = servers; + } + + public boolean isThereAnyPkgMissing() { + return isThereAnyPkgMissing; + } + + public boolean isThereAnyPkgNotSupported() { + return isThereAnyPkgNotSupported; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMetadata.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMetadata.java new file mode 100755 index 000000000..66fef5c14 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMetadata.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +public class DataGridMetadata implements Comparable { + + private String attribute; + + private String value; + + private String unit; + + public DataGridMetadata(){ + + } + + public DataGridMetadata(String attribute, String value, String unit){ + this.attribute = attribute; + this.value = value; + this.unit = unit; + } + + @Override + public boolean equals(Object obj){ + if(obj == null || !(obj instanceof DataGridMetadata)) return false; + + DataGridMetadata m = (DataGridMetadata) obj; + return attribute.equals(m.getAttribute()) && value.equals(m.getValue()) && unit.equals(m.getUnit()); + } + + public String getAttribute() { + return attribute; + } + + public void setAttribute(String attribute) { + this.attribute = attribute; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } + + @Override + public int compareTo(DataGridMetadata o) { + return this.attribute.compareToIgnoreCase(o.getAttribute()); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMetadataFields.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMetadataFields.java new file mode 100755 index 000000000..189ae9950 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMetadataFields.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +import javax.persistence.*; +import java.io.Serializable; + +@Entity +@Audited +@Table(name = "metadata_fields") +public class DataGridMetadataFields implements Serializable, Comparable { + + private static final long serialVersionUID = 1L; + + @Id + @NotAudited + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name = "id", unique = true, nullable = false) + private Long id; + + @Column(name = "attribute", length = 60) + private String attribute; + + @Column(name = "attribute_value", length = 60) + private String attributeValue; + + @Column(name = "attribute_unit", length = 60) + private String attributeUnit; + + @Column(name = "start_range") + private float startRange; + + @Column(name = "end_range") + private float endRange; + + @Column(name = "field_order") + private int order; + + @ManyToOne(fetch = FetchType.EAGER, optional=true) + @JoinColumn(name = "template_id", nullable = false, updatable = true) + private DataGridTemplate template; + + public DataGridMetadataFields() { + + } + + public DataGridMetadataFields(String attribute, String value, String unit) { + this.attribute = attribute; + this.attributeValue = value; + this.attributeUnit = unit; + } + + /** + * @return the id + */ + public long getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(long id) { + this.id = id; + } + + /** + * @return the attribute + */ + public String getAttribute() { + return attribute; + } + + /** + * @param attribute the attribute to set + */ + public void setAttribute(String attribute) { + this.attribute = attribute; + } + + /** + * @return the value + */ + public String getValue() { + return attributeValue; + } + + /** + * @param value the value to set + */ + public void setValue(String value) { + this.attributeValue = value; + } + + /** + * @return the unit + */ + public String getUnit() { + return attributeUnit; + } + + /** + * @param unit the unit to set + */ + public void setUnit(String unit) { + this.attributeUnit = unit; + } + + @Override + public int compareTo(DataGridMetadataFields dgmf) { + return this.attributeValue.compareTo(dgmf.getValue()); + } + + + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMetadataSearch.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMetadataSearch.java new file mode 100755 index 000000000..5aa2857b4 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridMetadataSearch.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.entity.enums.DataGridSearchOperatorEnum; + +/** + * This is a definition of metadata search criteria. A search criteria is broken into an object to + * manipulate its attribute, operator, and value. + * + */ +public class DataGridMetadataSearch { + + private String attribute; + private DataGridSearchOperatorEnum operator; + private String value; + private String unit; + private String regex = "([^A-Za-z0-9-_.,:=! ]+)"; + private String attrColName = "m.meta_attr_name"; + private String valueColName = "m.meta_attr_value"; + private String unitColName = "m.meta_attr_unit"; + + public DataGridMetadataSearch(String attribute, String value, String unit, + DataGridSearchOperatorEnum operator) { + this.attribute = attribute; + this.operator = operator; + this.value = value; + this.unit = unit; + } + + /** + * Builds a SQL query string to look for a piece of metadata (attribute, operator, and value). + * + * @return SQL query string + */ + public String getSpecQueryAsString() { + String attr = this.attribute.replaceAll(regex, ""); + String opt = this.operator.toString().replaceAll(regex, ""); + String val = this.value.replaceAll(regex, ""); + String unit = this.unit.replaceAll(regex, ""); + + boolean hasAttr = !attr.isEmpty(); + boolean hasVal = !val.isEmpty(); + boolean hasUnit = !unit.isEmpty(); + + val = addSQLCharToQueryParamBasedOnOperator(val); + unit = addSQLCharToQueryParamBasedOnOperator(unit); + + String attrQuery = hasAttr ? String.format(" LOWER( %s ) = LOWER( '%s' ) ", attrColName, attr) : ""; + String valueQuery = hasVal ? String.format(" LOWER( %s ) %s LOWER( %s ) ", valueColName, opt, val) : ""; + String unitQuery = hasUnit ? String.format(" LOWER( %s ) %s LOWER( %s ) ", unitColName, opt, unit) : ""; + + if (hasAttr && (hasVal || hasUnit)) { + attrQuery = String.format(" %s AND ", attrQuery); + } + + if (hasVal && hasUnit) { + valueQuery = String.format(" %s AND ", valueQuery); + } + + StringBuilder sb = new StringBuilder(); + sb.append(" SELECT map.object_id AS map_object_id "); + sb.append(" FROM r_objt_metamap map "); + sb.append(" JOIN ( "); + sb.append(" SELECT m.meta_id, m.meta_attr_name, m.meta_attr_value"); + sb.append(" FROM r_meta_main m "); + sb.append(" WHERE "); + sb.append(attrQuery); + sb.append(valueQuery); + sb.append(unitQuery); + sb.append(" )"); + sb.append(" AS metadata ON (metadata.meta_id = map.meta_id)"); + sb.append(" GROUP BY map.object_id"); + sb.append(" HAVING COUNT(map.meta_id) > 0 "); + return sb.toString(); + } + + @Override + public String toString() { + return String.format("%s %s %s", this.attribute, this.operator.toString(), this.value); + } + + /** + * Based on the operator applied to the query (LIKE or NOT LIKE), this method checks if any + * SQL special character needs to be added to the parameter in order for the query to run + * properly. + * + * @param param + * @return String representing the given parameters along with the proper SQL character for the + * query. + */ + private String addSQLCharToQueryParamBasedOnOperator(String param) { + if (this.operator == DataGridSearchOperatorEnum.LIKE + || this.operator == DataGridSearchOperatorEnum.NOT_LIKE) { + return String.format(" '%%%s%%' ", param); + } + return String.format(" '%s' ", param); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridPageContext.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridPageContext.java new file mode 100755 index 000000000..b3832b78f --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridPageContext.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +public class DataGridPageContext { + + private int startItemNumber; + private int endItemNumber; + private int totalNumberOfItems; + + /** + * @return the startItemNumber + */ + public int getStartItemNumber() { + return startItemNumber; + } + /** + * @param startItemNumber the startItemNumber to set + */ + public void setStartItemNumber(int startItemNumber) { + this.startItemNumber = startItemNumber; + } + /** + * @return the endItemNumber + */ + public int getEndItemNumber() { + return endItemNumber; + } + /** + * @param endItemNumber the endItemNumber to set + */ + public void setEndItemNumber(int endItemNumber) { + this.endItemNumber = endItemNumber; + } + /** + * @return the totalNumberOfItems + */ + public int getTotalNumberOfItems() { + return totalNumberOfItems; + } + /** + * @param totalNumberOfItems the totalNumberOfItems to set + */ + public void setTotalNumberOfItems(int totalNumberOfItems) { + this.totalNumberOfItems = totalNumberOfItems; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridResource.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridResource.java new file mode 100755 index 000000000..d2c02c53b --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridResource.java @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class DataGridResource implements Serializable, Comparable { + + // resource id in the data grid + private long id; + + // resource name ("demoResc") + private String name; + + // resource zone name + private String zone; + + // resource type ("unix file system", "replication", etc) + private String type; + + // resource path + private String path; + + // resource free space + private long freeSpace; + + // when the free space was calculated + private Date freeSpaceTimeStamp; + + // other resources existing inside this resource + private List children; + + // resource parent name + private String parent; + + // resource status ("up", "down") + private String status; + + // resource host name + private String host; + + // when the resource was created + private Date createTime; + + // last time the resource was modified + private Date modifyTime; + + // any information related to this resource + private String info; + + // number of records existing in the resource + private int totalRecords; + + // comment about a resource + private String comment; + + // Context string of a resource + private String contextString; + + private static final long serialVersionUID = 1L; + + public DataGridResource(long id, String name, String zone, String type, String path, long freeSpace, Date freeSpaceTimeStamp, + List children, String parent, String status, String host, Date createTime, Date modifyTime, String info, int totalRecords, + String contextString) { + super(); + this.id = id; + this.name = name; + this.zone = zone; + this.type = type; + this.path = path; + this.freeSpace = freeSpace; + this.freeSpaceTimeStamp = freeSpaceTimeStamp; + this.children = children; + this.parent = parent; + this.status = status; + this.host = host; + this.contextString = contextString; + } + + public DataGridResource(long id, String name, String zone, String type, String path) { + this(id, name, zone, type, "unknown", -1, null, null, "unknown", "unknown", "unknown", new Date(), new Date(), "unknown", 0, null); + } + + public DataGridResource() { + // empty constructor + } + + @Override + public String toString() { + return "Resource Info" + "\n id=" + id + "\n name= " + name + "\n zone= " + zone + "\n type= " + type + "\n path= " + path + "\n freeSpace= " + + freeSpace + "\n freeSpaceDate= " + freeSpaceTimeStamp + "\n children= " + children + "\n parent= " + parent + "\n status= " + + status + "\n host= " + host + "\n context= " + contextString; + } + + /** + * @return the id + */ + public long getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(long id) { + this.id = id; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the zone + */ + public String getZone() { + return zone; + } + + /** + * @param zone + * the zone to set + */ + public void setZone(String zone) { + this.zone = zone; + } + + /** + * @return the type + */ + public String getType() { + return type; + } + + /** + * @param type + * the type to set + */ + public void setType(String type) { + this.type = type; + } + + /** + * @return the path + */ + public String getPath() { + return path; + } + + /** + * @param path + * the path to set + */ + public void setPath(String path) { + this.path = path; + } + + /** + * @return the freeSpace + */ + public long getFreeSpace() { + return freeSpace; + } + + /** + * @param freeSpace + * the freeSpace to set + */ + public void setFreeSpace(long freeSpace) { + this.freeSpace = freeSpace; + } + + /** + * @return the freeSpaceDate + */ + public Date getFreeSpaceDate() { + return freeSpaceTimeStamp; + } + + /** + * @param freeSpaceDate + * the freeSpaceDate to set + */ + public void setFreeSpaceDate(Date freeSpaceDate) { + freeSpaceTimeStamp = freeSpaceDate; + } + + /** + * @return the children + */ + public List getChildren() { + if (children == null) return new ArrayList<>(); + return children; + } + + /** + * @param children + * the children to set + */ + public void setChildren(List children) { + this.children = children; + } + + public void addChildResc(String childResc) { + if (children == null) children = new ArrayList<>(); + children.add(childResc); + } + + /** + * @return the parent + */ + public String getParent() { + if (parent == null) return ""; + return parent; + } + + /** + * @param parent + * the parent to set + */ + public void setParent(String parent) { + this.parent = parent; + } + + /** + * @return the status + */ + public String getStatus() { + return status; + } + + /** + * @param status + * the status to set + */ + public void setStatus(String status) { + this.status = status; + } + + /** + * @return the host + */ + public String getHost() { + return host; + } + + /** + * @param host + * the host to set + */ + public void setHost(String host) { + this.host = host; + } + + /** + * @return the freeSpaceTimeStamp + */ + public Date getFreeSpaceTimeStamp() { + return freeSpaceTimeStamp; + } + + /** + * @param freeSpaceTimeStamp + * the freeSpaceTimeStamp to set + */ + public void setFreeSpaceTimeStamp(Date freeSpaceTimeStamp) { + this.freeSpaceTimeStamp = freeSpaceTimeStamp; + } + + /** + * @return the createTime + */ + public Date getCreateTime() { + return createTime; + } + + /** + * @param createTime + * the createTime to set + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + /** + * @return the modifyTime + */ + public Date getModifyTime() { + return modifyTime; + } + + /** + * @param modifyTime + * the modifyTime to set + */ + public void setModifyTime(Date modifyTime) { + this.modifyTime = modifyTime; + } + + /** + * @return the info + */ + public String getInfo() { + return info; + } + + /** + * @param info + * the info to set + */ + public void setInfo(String info) { + this.info = info; + } + + /** + * @return the totalRecords + */ + public int getTotalRecords() { + return totalRecords; + } + + /** + * @param totalRecords + * the totalRecords to set + */ + public void setTotalRecords(int totalRecords) { + this.totalRecords = totalRecords; + } + + /** + * @return the comment + */ + public String getComment() { + return comment; + } + + /** + * @param comment + * the comment to set + */ + public void setComment(String comment) { + this.comment = comment; + } + + /** + * @return the contextString + */ + public String getContextString() { + return contextString; + } + + /** + * @param contextString + * the contextString to set + */ + public void setContextString(String contextString) { + this.contextString = contextString; + } + + /** + * @return the isiHost + */ + public String getIsiHost() { + String isiHost = ""; + + if (contextString == null || contextString.isEmpty()) { + return ""; + } + + if (contextString.contains("isi_host")) { + String[] contextStringSplitted = contextString.split(";"); + // checking if after splitting we have the parts: isi_host, isi_port, isi_user + if (contextStringSplitted.length == 3) { + // isi_host=, getting the host_ip value + if (contextStringSplitted[0].length() == 2) { + isiHost = contextStringSplitted[0].split("=")[1]; + } + else { + isiHost = null; + } + } + } + + return isiHost; + } + + /** + * @return the isiPort + */ + public String getIsiPort() { + String isiPort = ""; + + if (contextString == null || contextString.isEmpty()) { + return ""; + } + + if (contextString.contains("isi_port")) { + String[] contextStringSplitted = contextString.split(";"); + // checking if after splitting we have the parts: isi_host, isi_port, isi_user + if (contextStringSplitted.length == 3) { + // isi_port=, getting the port value + isiPort = contextStringSplitted[1].split("=")[1]; + } + } + + return isiPort; + } + + /** + * @return the isiUser + */ + public String getIsiUser() { + String isiUser = ""; + + if (contextString == null || contextString.isEmpty()) { + return ""; + } + + if (contextString.contains("isi_user")) { + String[] contextStringSplitted = contextString.split(";"); + // checking if after splitting we have the parts: isi_host, isi_port, isi_user + if (contextStringSplitted.length == 3) { + // isi_user=, getting the username value + isiUser = contextStringSplitted[2].split("=")[1]; + } + } + + return isiUser; + } + + /** + * Checks whether the resource is a root resource or not. Root resource is a resource + * that is not a child of any other resource. + * + * @return + */ + public boolean isFirstLevelResc() { + String parent = getParent(); + boolean isFirstLevel = false; + + if (parent == null || parent.isEmpty() || parent.equals(zone)) { + isFirstLevel = true; + } + + return isFirstLevel; + } + + @Override + public int compareTo(DataGridResource dataGridResource) { + return getName().toLowerCase().compareTo(dataGridResource.getName().toLowerCase()); + } + + @Override + public boolean equals(Object object) { + boolean isEqual = false; + + if (object != null && object instanceof DataGridResource) { + isEqual = id == ((DataGridResource) object).getId(); + } + + return isEqual; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridResourceType.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridResourceType.java new file mode 100755 index 000000000..cce6e794d --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridResourceType.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.entity.enums.DataGridResourceTypeEnum; + +public class DataGridResourceType implements Comparable { + private DataGridResourceTypeEnum dataGridType; + private String dataGridResourceTypeName; + private String dataGridResourceTypeNamePrettified; + + public DataGridResourceType() { + + } + + public DataGridResourceType(String dataGridResourceTypeNamePrettified, + String dataGridResourceTypeName, DataGridResourceTypeEnum dataGridType) { + this.dataGridResourceTypeNamePrettified = dataGridResourceTypeNamePrettified; + this.dataGridType = dataGridType; + this.dataGridResourceTypeName = dataGridResourceTypeName; + } + + @Override + public int compareTo(DataGridResourceType dgrt) { + return this.getDataGridResourceTypeName().compareTo(dgrt.getDataGridResourceTypeName()); + } + + /** + * @return the dataGridType + */ + public DataGridResourceTypeEnum getDataGridType() { + return dataGridType; + } + + /** + * @param dataGridType + * the dataGridType to set + */ + public void setDataGridType(DataGridResourceTypeEnum dataGridType) { + this.dataGridType = dataGridType; + } + + /** + * @return the dataGridTypeName + */ + public String getDataGridResourceTypeName() { + return dataGridResourceTypeName; + } + + /** + * @param dataGridTypeName + * the dataGridTypeName to set + */ + public void setDataGridResourceTypeName(String dataGridResourceTypeName) { + this.dataGridResourceTypeName = dataGridResourceTypeName; + } + + /** + * @return the dataGridResourceTypeNamePrettified + */ + public String getDataGridResourceTypeNamePrettified() { + return dataGridResourceTypeNamePrettified; + } + + /** + * @param dataGridResourceTypeNamePrettified + * the dataGridResourceTypeNamePrettified to set + */ + public void setDataGridResourceTypeNamePrettified(String dataGridResourceTypeNamePrettified) { + this.dataGridResourceTypeNamePrettified = dataGridResourceTypeNamePrettified; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridRule.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridRule.java new file mode 100755 index 000000000..8a7991801 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridRule.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import org.springframework.beans.factory.annotation.Value; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * iRODS Rule. + */ +public class DataGridRule { + @Value("${irods.host}") + private String iCATHost; + private String[] inputRuleParams; // rule input parameters + + private String[] outputRuleParams; // rule output parameters + private String host; + private String rule; + private boolean declareRuleOutputParams; + private static final String INPUT = "INPUT"; + + private static final String OUTPUT = "OUTPUT"; + private static final String RULE_EXEC_OUT = "ruleExecOut"; + private static final String RULE_INPUT_NULL = "null"; + public static final String GET_VERSION_RULE = "getVersion"; + + public static final String POPULATE_RULE = "populateMetadataForFile"; + public static final String JPG_RULE = "automaticJpgMetadataExtraction"; + public static final String VCF_RULE = "automaticVcfMetadataExtraction"; + public static final String BAM_CRAM_RULE = "automaticBamMetadataExtraction"; + public static final String XML_MANIFEST_RULE = "automaticExtractMetadataFromXMLManifest"; + public static final String REPL_DATA_OBJ_RULE = "replicateDataObjInAdminMode"; + public static final String ILLUMINA_RULE = "illuminaMetadataForFile"; + public static final String TAR_RULE = "extractTar"; + public static final String GET_MSIS_RULE = "getMicroservices"; + public static final String EMPTY_TRASH_RULE = "emptyTrash"; + public static final String DEPLOYMENT_RULE = "deploymentRule"; + + // Maps rules for their respective microservices + private static final Map rulesMap; + + static { + Map map = new HashMap<>(); + map.put(POPULATE_RULE, "msiobjput_populate"); + map.put(JPG_RULE, "msiobjjpeg_extract"); + map.put(VCF_RULE, "msiobjput_mdvcf"); + map.put(BAM_CRAM_RULE, "msiobjput_mdbam"); + map.put(XML_MANIFEST_RULE, "msiobjput_mdmanifest"); + map.put(REPL_DATA_OBJ_RULE, "msiDataObjRepl"); + map.put(GET_VERSION_RULE, "msiobjget_version"); + map.put(ILLUMINA_RULE, "msiget_illumina_meta"); + map.put(TAR_RULE, "msiTarFileExtract"); + map.put(GET_MSIS_RULE, "msiobjget_microservices"); + map.put(EMPTY_TRASH_RULE, "msiRmColl"); + map.put(DEPLOYMENT_RULE, "msirule_deployment"); + rulesMap = Collections.unmodifiableMap(map); + } + + public DataGridRule(String rule, String host, boolean declareRuleOutputParams) { + this.host = host; + this.rule = rule; + this.declareRuleOutputParams = declareRuleOutputParams; + } + + public DataGridRule(String rule, String host) { + this(rule, host, true); + } + + public void setInputRuleParams(String... params) { this.inputRuleParams = params; } + + public void setOutputRuleParams(String... params) { this.outputRuleParams = params; } + + public boolean declareRuleOutputParams() { + return declareRuleOutputParams; + } + + private String getInputParamsAsString() { + StringBuilder sb = new StringBuilder(); + + sb.append(INPUT); + if(inputRuleParams != null) { + String[] params = inputRuleParams; + for (int i = 0; i < params.length - 1; i++) sb.append(String.format(" *p%d=\"%s\",", i, params[i])); + sb.append(String.format(" *p%d=\"%s\"\n", params.length - 1, params[params.length - 1])); + } + else { + sb.append(" "); + sb.append(RULE_INPUT_NULL); + sb.append("\n"); + } + + return sb.toString(); + } + + private String getOutputParamsAsString() { + StringBuilder sb = new StringBuilder(); + + sb.append(OUTPUT); + + if(outputRuleParams != null) { + String[] params = outputRuleParams; + for (int i = 0; i < params.length - 1; i++) sb.append(String.format(" *%s,", params[i])); + sb.append(String.format(" *%s\n", params[params.length - 1])); + } + else { + sb.append(" "); + sb.append(RULE_EXEC_OUT); + sb.append("\n"); + } + + return sb.toString(); + } + + private String getMSIParamsAsString() { + StringBuilder paramsEscaped = new StringBuilder(); + paramsEscaped.append(" "); + paramsEscaped.append(rulesMap.get(rule)); + paramsEscaped.append("("); + + if(inputRuleParams != null) { + for (int i = 0; i < inputRuleParams.length - 1; i++) paramsEscaped.append(String.format("*p%d, ", i)); + paramsEscaped.append(String.format("*p%d", inputRuleParams.length - 1)); + + if(outputRuleParams != null) paramsEscaped.append(", "); + } + + if(outputRuleParams != null) { + for (int i = 0; i < outputRuleParams.length - 1; i++) paramsEscaped.append(String.format("*%s, ", outputRuleParams[i])); + paramsEscaped.append(String.format("*%s", outputRuleParams[outputRuleParams.length - 1])); + } + + paramsEscaped.append(");\n"); + + return paramsEscaped.toString(); + } + + private String initializeOutputParams() { + if(outputRuleParams == null) return ""; + + StringBuilder outputParams = new StringBuilder(); + outputParams.append(" "); + + for (int i = 0; i < outputRuleParams.length - 1; i++) outputParams.append(String.format("*%s=\"\",", outputRuleParams[i])); + outputParams.append(String.format("*%s=\"\";", outputRuleParams[outputRuleParams.length - 1])); + + return outputParams.toString(); + } + + public String toString() { + RemoteRuleHeader header = new RemoteRuleHeader(host); + StringBuilder ruleString = new StringBuilder(); + ruleString.append("\n"); + ruleString.append(rule); + ruleString.append("{"); + ruleString.append("\n"); + if(declareRuleOutputParams) ruleString.append(initializeOutputParams() + "\n"); + ruleString.append(header.getRemoteRuleHeader()); + ruleString.append(getMSIParamsAsString()); + ruleString.append(header.getRemoteRuleFooter()); + ruleString.append("}"); + ruleString.append("\n"); + ruleString.append(getInputParamsAsString()); + ruleString.append(getOutputParamsAsString()); + + return ruleString.toString(); + } + + private class RemoteRuleHeader { + private String remoteHeader = null; + private String remoteFooter = null; + + RemoteRuleHeader(String host) { + remoteHeader = String.format(" remote(\"%s\", \"\") {\n", host); + remoteFooter = " }\n"; + } + + String getRemoteRuleHeader() { + return this.remoteHeader; + } + + String getRemoteRuleFooter() { + return this.remoteFooter; + } + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridServer.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridServer.java new file mode 100755 index 000000000..9f2428fa3 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridServer.java @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.entity.enums.DataGridServerType; + +import java.util.*; + +public class DataGridServer implements Comparable { + + private DataGridServerType type; + private String hostname; + private String ip; + private String machineStatus; + private String dataGridStatus; + private String memoryStatus; + private String diskStatus; + private long totalStorageUsed; + private long totalStorageAvailable; + private long totalStorage; + private List resources; + private boolean isRmdPackageRunning; + private String rmdPackageRelease; + private String rmdPackageVersion; + private String msiVersion; + private List msisInstaleld; + + private List mlxMSIsExpected; + private List irodsMSIsExpected; + private List otherMSIsExpected; + + private Map metalnxMSIs; + private Map irodsMSIs; + private Map otherMSIs; + + public DataGridServer() { + metalnxMSIs = new HashMap<>(); + irodsMSIs = new HashMap<>(); + otherMSIs = new HashMap<>(); + } + + /** + * @return the type + */ + public DataGridServerType getType() { + return type; + } + + /** + * @return the hostname + */ + public String getHostname() { + return hostname; + } + + /** + * Gets the resources of this server sorted by resource name + * @return the resources + */ + public List getResources() { + if(resources != null) Collections.sort(resources); + return resources; + } + + /** + * @param type + * the type to set + */ + public void setType(DataGridServerType type) { + this.type = type; + } + + /** + * @param hostname + * the hostname to set + */ + public void setHostname(String hostname) { + this.hostname = hostname; + } + + /** + * @param resources + * the resources to set + */ + public void setResources(List resources) { + this.resources = resources; + } + + /** + * Adds resource to the server + * @param resource to be added + */ + public void addResource(DataGridResource resource) { + if (this.resources == null) { + this.resources = new ArrayList<>(); + } + this.resources.add(resource); + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + /** + * @return the machineStatus + */ + public String getMachineStatus() { + return machineStatus; + } + + /** + * @return the dataGridStatus + */ + public String getDataGridStatus() { + return dataGridStatus; + } + + /** + * @return the memoryStatus + */ + public String getMemoryStatus() { + return memoryStatus; + } + + /** + * @return the diskStatus + */ + public String getDiskStatus() { + return diskStatus; + } + + /** + * @param machineStatus the machineStatus to set + */ + public void setMachineStatus(String machineStatus) { + this.machineStatus = machineStatus; + } + + /** + * @param dataGridStatus the dataGridStatus to set + */ + public void setDataGridStatus(String dataGridStatus) { + this.dataGridStatus = dataGridStatus; + } + + /** + * @param memoryStatus the memoryStatus to set + */ + public void setMemoryStatus(String memoryStatus) { + this.memoryStatus = memoryStatus; + } + + /** + * @param diskStatus the diskStatus to set + */ + public void setDiskStatus(String diskStatus) { + this.diskStatus = diskStatus; + } + + /** + * @return the totalStorageUsed + */ + public long getTotalStorageUsed() { + return totalStorageUsed; + } + + /** + * @return the totalStorageAvailable + */ + public long getTotalStorageAvailable() { + return totalStorageAvailable; + } + + /** + * @return the totalStorage + */ + public long getTotalStorage() { + return totalStorage; + } + + /** + * @param totalStorageUsed the totalStorageUsed to set + */ + public void setTotalStorageUsed(long totalStorageUsed) { + this.totalStorageUsed = totalStorageUsed; + } + + /** + * @param totalStorageAvailable the totalStorageAvailable to set + */ + public void setTotalStorageAvailable(long totalStorageAvailable) { + this.totalStorageAvailable = totalStorageAvailable; + } + + /** + * @param totalStorage the totalStorage to set + */ + public void setTotalStorage(long totalStorage) { + this.totalStorage = totalStorage; + } + + /** + * @return the isRmdPackageRunning + */ + public boolean isRmdPackageRunning() { + return isRmdPackageRunning; + } + + /** + * @param isRmdPackageRunning the isRmdPackageRunning to set + */ + public void setRmdPackageRunning(boolean isRmdPackageRunning) { + this.isRmdPackageRunning = isRmdPackageRunning; + } + + /** + * This method compares if two DataGridServer objects are the same. They will be the same + * if they have the same host name. + * @return true, if they are equal. False, otherwise. + */ + @Override + public boolean equals(Object obj) { + if(obj == null) { + return false; + } + + if(this == obj) { + return true; + } + + if(obj instanceof DataGridServer) { + String hostName = ((DataGridServer) obj).getHostname(); + return this.getHostname().equals(hostName) ; + } + + return false; + } + + /** + * This method provides consistent results for the equals method. If two DataGridServer objects + * are equal, they both must have the same hash code. + * @return a hash code based on the host name + */ + @Override + public int hashCode() { + return this.getHostname().hashCode(); + } + + @Override + public int compareTo(DataGridServer dgs) { + return this.ip.compareTo(dgs.getIp()); + } + + /** + * @return the rmdPackageRelease + */ + public String getRmdPackageRelease() { + return rmdPackageRelease; + } + + /** + * @param rmdPackageRelease the rmdPackageRelease to set + */ + public void setRmdPackageRelease(String rmdPackageRelease) { + this.rmdPackageRelease = rmdPackageRelease; + } + + /** + * @return the rmdPackageVersion + */ + public String getRmdPackageVersion() { + return rmdPackageVersion; + } + + /** + * @param rmdPackageVersion the rmdPackageVersion to set + */ + public void setRmdPackageVersion(String rmdPackageVersion) { + this.rmdPackageVersion = rmdPackageVersion; + } + + public void setMSIVersion(String msiVersion) { this.msiVersion = msiVersion; } + + public String getMSIVersion() { + if(msiVersion == null ){ + return ""; + } + return msiVersion; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getHostname()); + sb.append(" - "); + sb.append(getIp()); + sb.append(" - "); + sb.append(getMSIVersion()); + return sb.toString(); + } + + public void setMSIInstalledList(List msisInstalled) { + if(msisInstalled == null || msisInstalled.isEmpty()) return; + + this.msisInstaleld = msisInstalled; + + // classifying MSIs by their type + for(String msi: msisInstalled) { + if(mlxMSIsExpected.contains(msi)) addToMsiMetalnx(msi); + else if(irodsMSIsExpected.contains(msi)) addToMsiIRODS(msi); + else addToMsiOther(msi); + } + } + + public List getMSIInstalledList() { + return this.msisInstaleld != null ? this.msisInstaleld : new ArrayList<>(); + } + + public void setMetalnxExpectedMSIs(List mlxMSIsExpected) { + if(mlxMSIsExpected == null || mlxMSIsExpected.isEmpty()) return; + + this.mlxMSIsExpected = mlxMSIsExpected; + for(String msi: mlxMSIsExpected) { + if (!msi.isEmpty()) metalnxMSIs.put(msi, false); + } + } + + public void setIRodsExpectedMSIs(List irodsMSIsExpected) { + if(irodsMSIsExpected == null || irodsMSIsExpected.isEmpty()) return; + + this.irodsMSIsExpected = irodsMSIsExpected; + for(String msi: irodsMSIsExpected) { + if (!msi.isEmpty()) irodsMSIs.put(msi, false); + } + } + + public void setOtherExpectedMSIs(List otherMSIsExpected) { + if(otherMSIsExpected == null || otherMSIsExpected.isEmpty()) return; + + this.otherMSIsExpected = otherMSIsExpected; + for(String msi: otherMSIsExpected) { + if (!msi.isEmpty()) otherMSIs.put(msi, false); + } + } + + // used by frontend + public Map getMetalnxMSIs() { return metalnxMSIs; } + + // used by frontend + public Map getIRODSMSIs() { return irodsMSIs; } + + // used by frontend + public Map getOtherMSIs() { return otherMSIs; } + + // used by frontend + public boolean isThereAnyMSI() { + return !getMSIInstalledList().isEmpty(); + } + + private void addToMsiMetalnx(String msi) { + if(msi == null || msi.isEmpty()) return; + this.metalnxMSIs.put(msi, true); + } + + private void addToMsiIRODS(String msi) { + if(msi == null || msi.isEmpty()) return; + this.irodsMSIs.put(msi, true); + } + + private void addToMsiOther(String msi) { + if(msi == null || msi.isEmpty()) return; + this.otherMSIs.put(msi, true); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridSpecificQuery.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridSpecificQuery.java new file mode 100755 index 000000000..a2008381f --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridSpecificQuery.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +public class DataGridSpecificQuery { + + private String alias; + private String query; + + /** + * @return the alias + */ + public String getAlias() { + return alias; + } + + /** + * @return the query + */ + public String getQuery() { + return query; + } + + /** + * @param alias the alias to set + */ + public void setAlias(String alias) { + this.alias = alias; + } + + /** + * @param query the query to set + */ + public void setQuery(String query) { + this.query = query; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridTemplate.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridTemplate.java new file mode 100755 index 000000000..6d83069c6 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridTemplate.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.exceptions.DataGridTooLongTemplateNameException; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; +import java.util.Set; + +@Entity +@Audited +@Table(name = "templates") +public class DataGridTemplate implements Serializable, Comparable { + + private static final long serialVersionUID = 1L; + + @Id + @NotAudited + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "template_id", unique = true, nullable = false) + private Long id; + + @Column(name = "template_name", unique = true, nullable = false, length = 100) + private String templateName; + + @Column(name = "owner", nullable = false, length = 100) + private String owner; + + @Column(name = "description", nullable = false, length = 512) + private String description; + + @Column(name = "usage_info", length = 100) + private String usageInformation; + + @Column(name = "access_type", length = 32) + private String accessType; + + @Column(name = "version") + private Integer version; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "create_ts", nullable = false, length = 60, updatable = false) + private Date createTs; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "modify_ts", nullable = false, length = 60) + private Date modifyTs; + + @OneToMany(mappedBy = "template", fetch = FetchType.EAGER) + private Set fields; + + private static final int TEMPLATE_NAME_MAX_LENGTH = 100; + private static final int TEMPLATE_DESC_MAX_LENGTH = 100; + + private boolean isModified = false; + + public DataGridTemplate() { + // empty constructor + } + + public DataGridTemplate(String templateName) throws DataGridTooLongTemplateNameException { + if (templateName.length() > TEMPLATE_NAME_MAX_LENGTH) { + throw new DataGridTooLongTemplateNameException("Template name exceeded " + TEMPLATE_NAME_MAX_LENGTH + " characters."); + } + + this.templateName = templateName; + } + + /** + * @return the owner + */ + public String getOwner() { + return owner; + } + + /** + * @param owner + * the owner to set + */ + public void setOwner(String owner) { + this.owner = owner; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description + * the description to set + * @throws DataGridTooLongTemplateNameException + */ + public void setDescription(String description) throws DataGridTooLongTemplateNameException { + if (description.length() > TEMPLATE_NAME_MAX_LENGTH) { + throw new DataGridTooLongTemplateNameException("Template description exceeded " + TEMPLATE_DESC_MAX_LENGTH + " characters."); + } + this.description = description; + } + + /** + * @return the createTs + */ + public Date getCreateTs() { + return createTs; + } + + /** + * @param createTs + * the createTs to set + */ + public void setCreateTs(Date createTs) { + this.createTs = createTs; + } + + /** + * @return the modifyTs + */ + public Date getModifyTs() { + return modifyTs; + } + + /** + * @param modifyTs + * the modifyTs to set + */ + public void setModifyTs(Date modifyTs) { + this.modifyTs = modifyTs; + } + + /** + * @return the id + */ + public long getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(long id) { + this.id = id; + } + + /** + * @return the templateName + */ + public String getTemplateName() { + return templateName; + } + + /** + * @param the + * templateName to set + * @throws DataGridTooLongTemplateNameException + */ + public void setTemplateName(String templateName) throws DataGridTooLongTemplateNameException { + if (templateName.length() > TEMPLATE_NAME_MAX_LENGTH) { + throw new DataGridTooLongTemplateNameException("Template name exceeded " + TEMPLATE_NAME_MAX_LENGTH + " characters."); + } + + this.templateName = templateName; + } + + /** + * @return the usageInformation + */ + public String getUsageInformation() { + return usageInformation; + } + + /** + * @param usageInformation + * the usageInformation to set + */ + public void setUsageInformation(String usageInformation) { + this.usageInformation = usageInformation; + } + + /** + * @return the accessType + */ + public String getAccessType() { + return accessType; + } + + /** + * @param accessType + * the accessType to set + */ + public void setAccessType(String accessType) { + this.accessType = accessType; + } + + /** + * @return the fields + */ + public Set getFields() { + return fields; + } + + /** + * @param fields + * the fields to set + */ + public void setFields(Set fields) { + this.fields = fields; + } + + /** + * @return the version + */ + public Integer getVersion() { + return version; + } + + /** + * @param version + * the version to set + */ + public void setVersion(Integer version) { + this.version = version; + } + + /** + * @return the isModified + */ + public boolean isModified() { + return isModified; + } + + /** + * @param isModified + * the isModified to set + */ + public void setModified(boolean isModified) { + this.isModified = isModified; + } + + @Override + public int compareTo(DataGridTemplate dgt) { + return templateName.compareTo(dgt.getTemplateName()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Template: "); + sb.append(templateName); + sb.append("\nDescription: "); + sb.append(description); + sb.append("\nOwner: "); + sb.append(owner); + sb.append("\nAccess Type: "); + sb.append(accessType); + sb.append("\nCreated: "); + sb.append(createTs); + sb.append("\nModified: "); + sb.append(modifyTs); + sb.append("\n"); + return sb.toString(); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridTemplateField.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridTemplateField.java new file mode 100755 index 000000000..fd96c80a6 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridTemplateField.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateAttrException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateUnitException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateValueException; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +import javax.persistence.*; +import java.io.Serializable; + +@Entity +@Audited +@Table(name = "template_fields") +public class DataGridTemplateField implements Serializable, Comparable { + + private static final long serialVersionUID = 1L; + + private final int MAX_ATTR_LENGTH = 100; + private final int MAX_VAL_LENGTH = 100; + private final int MAX_UNT_LENGTH = 100; + + @Id + @NotAudited + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "template_field_id", unique = true, nullable = false) + private Long id; + + @Column(name = "attribute", length = MAX_ATTR_LENGTH) + private String attribute; + + @Column(name = "attribute_value", length = MAX_VAL_LENGTH) + private String attributeValue; + + @Column(name = "attribute_unit", length = MAX_UNT_LENGTH) + private String attributeUnit; + + @Column(name = "start_range") + private float startRange; + + @Column(name = "end_range") + private float endRange; + + @Column(name = "field_order") + private int order; + + @ManyToOne(fetch = FetchType.EAGER, optional = true) + @JoinColumn(name = "template_id", nullable = false, updatable = true) + private DataGridTemplate template; + + public DataGridTemplateField() { + + } + + public DataGridTemplateField(String attribute, String value, String unit, DataGridTemplate template) throws DataGridTemplateAttrException, + DataGridTemplateValueException, DataGridTemplateUnitException { + if (attribute == null || attribute.length() > MAX_ATTR_LENGTH) { + throw new DataGridTemplateAttrException("Template attribute is null or exceed " + MAX_ATTR_LENGTH + " characters."); + } + + if (value == null || value.length() > MAX_VAL_LENGTH) { + throw new DataGridTemplateValueException("Template attribute is null or exceed " + MAX_ATTR_LENGTH + " characters."); + } + + if (unit == null || unit.length() > MAX_UNT_LENGTH) { + throw new DataGridTemplateUnitException("Template attribute is null or exceed " + MAX_ATTR_LENGTH + " characters."); + } + + this.attribute = attribute; + attributeValue = value; + attributeUnit = unit; + this.template = template; + } + + public DataGridTemplateField(String attribute, String value, String unit) throws DataGridTemplateAttrException, DataGridTemplateValueException, + DataGridTemplateUnitException { + this(attribute, value, unit, null); + } + + /** + * @return the id + */ + public Long getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(long id) { + this.id = id; + } + + /** + * @return the attribute + */ + public String getAttribute() { + return attribute; + } + + /** + * @param attribute + * the attribute to set + * @throws DataGridTemplateAttrException + */ + public void setAttribute(String attribute) throws DataGridTemplateAttrException { + if (attribute == null || attribute.length() > MAX_ATTR_LENGTH) { + throw new DataGridTemplateAttrException("Template attribute is null or exceed " + MAX_ATTR_LENGTH + " characters."); + } + + this.attribute = attribute; + } + + /** + * @return the value + */ + public String getValue() { + return attributeValue; + } + + /** + * @param value + * the value to set + * @throws DataGridTemplateValueException + */ + public void setValue(String value) throws DataGridTemplateValueException { + if (value == null || value.length() > MAX_VAL_LENGTH) { + throw new DataGridTemplateValueException("Template attribute is null or exceed " + MAX_ATTR_LENGTH + " characters."); + } + + attributeValue = value; + } + + /** + * @return the unit + */ + public String getUnit() { + return attributeUnit; + } + + /** + * @param unit + * the unit to set + * @throws DataGridTemplateUnitException + */ + public void setUnit(String unit) throws DataGridTemplateUnitException { + if (unit == null || unit.length() > MAX_UNT_LENGTH) { + throw new DataGridTemplateUnitException("Template attribute is null or exceed " + MAX_ATTR_LENGTH + " characters."); + } + + attributeUnit = unit; + } + + /** + * @return the template + */ + public DataGridTemplate getTemplate() { + return template; + } + + /** + * @param template + * the template to set + */ + public void setTemplate(DataGridTemplate template) { + this.template = template; + } + + /** + * @return the startRange + */ + public float getStartRange() { + return startRange; + } + + /** + * @param startRange + * the startRange to set + */ + public void setStartRange(float startRange) { + this.startRange = startRange; + } + + /** + * @return the endRange + */ + public float getEndRange() { + return endRange; + } + + /** + * @param endRange + * the endRange to set + */ + public void setEndRange(float endRange) { + this.endRange = endRange; + } + + /** + * @return the order + */ + public int getOrder() { + return order; + } + + /** + * @param order + * the order to set + */ + public void setOrder(int order) { + this.order = order; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Id: "); + sb.append(id); + sb.append("\nAttribute: "); + sb.append(getAttribute()); + sb.append("\nValue: "); + sb.append(getValue()); + sb.append("\nUnit: "); + sb.append(getUnit()); + sb.append("\nOrder: "); + sb.append(getOrder()); + sb.append("\nStart Range: "); + sb.append(getStartRange()); + sb.append("\nEnd Range: "); + sb.append(getEndRange()); + sb.append("\nTemplate: "); + sb.append(getTemplate().getTemplateName()); + return sb.toString(); + } + + @Override + public int compareTo(DataGridTemplateField dgmf) { + if (attribute != null) { + return attribute.compareTo(dgmf.getAttribute()); + } + else if (attributeValue != null) { + return attributeValue.compareTo(dgmf.getValue()); + } + else if (attributeUnit != null) { + return attributeUnit.compareTo(dgmf.getUnit()); + } + + return 0; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj instanceof DataGridTemplateField) { + DataGridTemplateField dgmf = (DataGridTemplateField) obj; + + if (dgmf.getAttribute() == null || dgmf.getValue() == null) { + return false; + } + + boolean areAttributesEqual = getAttribute().equals(dgmf.getAttribute()); + boolean areValuesEqual = getValue().equals(dgmf.getValue()); + + if (areAttributesEqual && areValuesEqual) { + return true; + } + } + + return false; + } + + @Override + public int hashCode() { + return (getAttribute() + getValue()).hashCode(); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridTicket.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridTicket.java new file mode 100755 index 000000000..82f872833 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridTicket.java @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.utils.DataGridJsonDateDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Serializable; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Class that represents a ticket. + */ +public class DataGridTicket implements Serializable { + private static final Logger logger = LoggerFactory.getLogger(DataGridTicket.class); + public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + private String ticketString, path, owner; + private TicketType type; + private boolean isCollection; + private int usesLimit; + private int usesCount; + private long writeByteLimit; + private long writeByteCount; + private int writeFileLimit; + private int writeFileCount; + + @JsonDeserialize(using = DataGridJsonDateDeserializer.class) + private Date expirationDate; + + private String expirationDateStr; + + // Ticket restrictions + private List hosts; + private List users; + private List groups; + + public enum TicketType { + READ, WRITE, UNKNOWN; + } + + /** + * Empty constructor + */ + public DataGridTicket() { + this(""); + } + + /** + * Constructor. + * @param path path in the grid that the ticket + */ + public DataGridTicket(String path) { + this.path = path; + ticketString = ""; + owner = ""; + expirationDateStr = ""; + type = TicketType.READ; + usesLimit = 0; + writeByteLimit = 0; + hosts = new ArrayList<>(); + users = new ArrayList<>(); + groups = new ArrayList<>(); + } + + public void addHost(String newHost) { + if(hosts == null) hosts = new ArrayList<>(); + if(!hosts.contains(newHost)) hosts.add(newHost); + } + + public void addUser(String user) { + if(users == null) users = new ArrayList<>(); + if(!users.contains(user)) users.add(user); + } + + public void addGroup(String group) { + if(groups == null) groups = new ArrayList<>(); + if(!groups.contains(group)) groups.add(group); + } + + public void setGroups(List groups) { + this.groups = groups; + } + + public void setUsers(List users) { + this.users = users; + } + + public void setHosts(List hosts) { + this.hosts = hosts; + } + + public void setWriteFileLimit(int writeFileLimit) { + this.writeFileLimit = writeFileLimit; + } + + public void setWriteFileCount(int writeFileCount) { + this.writeFileCount = writeFileCount; + } + + public void setWriteByteLimit(long writeByteLimit) { + this.writeByteLimit = writeByteLimit; + } + + public void setWriteByteCount(long writeByteCount) { + this.writeByteCount = writeByteCount; + } + + public void setUsesCount(int usesCount) { + this.usesCount = usesCount; + } + + public void setExpirationDate(Date expirationDate) { + this.expirationDate = expirationDate; + } + + public void setExpirationDateStr(String expirationDateStr) { + if (expirationDateStr == null) { + this.expirationDateStr = ""; + return; + } + this.expirationDateStr = expirationDateStr; + } + + public void setUsesLimit(int usesLimit) { + this.usesLimit = usesLimit; + } + + public void setTicketString(String ticketString) { + this.ticketString = ticketString; + } + + public void setPath(String path) { + this.path = path; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public void setType(TicketType type) { + this.type = type; + } + + /** + * Tells whether or not the ticket is for a collection. + * @param isTicketForCollection True if the path associated to the ticket is a collection. False, otherwise. + */ + public void setIsCollection(boolean isTicketForCollection) { + isCollection = isTicketForCollection; + } + + public List getGroups() { + if(groups == null) users = new ArrayList<>(); + return groups; + } + + public List getUsers() { + if(users == null) users = new ArrayList<>(); + return users; + } + + public List getHosts() { + if(hosts == null) users = new ArrayList<>(); + return hosts; + } + + public int getWriteFileLimit() { + return writeFileLimit; + } + + public int getWriteFileCount() { + return writeFileCount; + } + + public long getWriteByteCount() { + return writeByteCount; + } + + public long getWriteByteLimit() { + return writeByteLimit; + } + + public int getUsesCount() { + return usesCount; + } + + public String getExpirationDateStr() { + if (expirationDate != null) { + expirationDateStr = dateFormat.format(expirationDate); + } else if (expirationDateStr.isEmpty()) { + expirationDateStr = ""; + } + return expirationDateStr; + } + + public Date getExpirationDate() { + if (!expirationDateStr.isEmpty()) { + try { + expirationDate = dateFormat.parse(expirationDateStr); + } catch (ParseException e) { + logger.error("Could not parse expiration date"); + } + } + + return expirationDate; + } + + public int getUsesLimit() { + return usesLimit; + } + + public String getTicketString() { + if(ticketString == null) ticketString = ""; + return ticketString; + } + + public String getPath() { + if(path == null) path = ""; + return path; + } + + public String getOwner() { + if(owner == null) owner = ""; + return owner; + } + + public TicketType getType() { + return type; + } + + public boolean isCollection() { + return isCollection; + } + + @Override + public String toString() { + return "DataGridTicket{" + + "ticketString='" + ticketString + '\'' + + ", path='" + path + '\'' + + ", owner='" + owner + '\'' + + ", type=" + type + + ", isCollection=" + isCollection + + '}'; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUser.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUser.java new file mode 100755 index 000000000..095c9c76b --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUser.java @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +@Entity +@Audited +@Table(name = "users", uniqueConstraints = @UniqueConstraint(columnNames = { "username", "additional_info" }) ) +public class DataGridUser implements Serializable, Comparable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @NotAudited + private Long id; + + @Column(name = "data_grid_id", unique = true, nullable = false) + private long dataGridId; + + @Column(name = "username", nullable = false, length = 60, unique = true) + private String username; + + private String password; + + @Column(name = "additional_info", nullable = true, length = 128) + private String additionalInfo; + + @Column(name = "enabled", nullable = false) + private boolean enabled; + + @Column(name = "first_name") + private String firstName; + + @Column(name = "last_name") + private String lastName; + + @Column(name = "email", nullable = true) + private String email; + + @Column(name = "locale") + private String locale = "en"; + + @Column(name = "forceFileOverwriting", nullable = false) + private boolean forceFileOverwriting = false; + + @Column(name = "user_type", nullable = false, length = 60) + private String userType; + + @Column(name = "organizational_role", nullable = true, length = 60) + private String organizationalRole; + + @ManyToOne(fetch = FetchType.EAGER, optional = true) + @JoinColumn(name = "userProfile_id", nullable = true, updatable = true) + private UserProfile userProfile; + + @Column(name = "user_company", nullable = true, length = 60) + private String company; + + @Column(name = "user_department", nullable = true, length = 60) + private String department; + + @Column(name = "user_title", nullable = true, length = 60) + private String title; + + @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.DETACH, orphanRemoval = true) + private Set bookmarks; + + @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.DETACH, orphanRemoval = true) + private Set favorites; + + private static final long serialVersionUID = -500578459147421831L; + + public DataGridUser() { + + } + + public DataGridUser(String username, String password, boolean enabled) { + this.username = username; + this.enabled = enabled; + } + + public String getDisplayName() { + if (firstName != null && !firstName.isEmpty()) { + return firstName; + } + + return username; + } + + /** + * Gets the user bookmarks sorted in ascending order + * + * @return the userBookmarks + */ + public List getBookmarksSorted() { + List bookmarksSorted = new ArrayList(bookmarks); + Collections.sort(bookmarksSorted); + return bookmarksSorted; + } + + /** + * @return the userBookmarks + */ + public Set getBookmarks() { + return bookmarks; + } + + /** + * @param userBookmarks + * the userBookmarks to set + */ + public void setUserBookmarks(Set userBookmarks) { + bookmarks = userBookmarks; + } + + /** + * Gets the user favorites sorted in ascending order + * + * @return the userFavorites + */ + public List getFavoritesSorted() { + List favoritesSorted = new ArrayList(favorites); + Collections.sort(favoritesSorted); + return favoritesSorted; + } + + /** + * @return the userFavorites + */ + public Set getFavorites() { + return favorites; + } + + /** + * @param userFavorites + * the userFavorites to set + */ + public void setUserFavorites(Set userFavorites) { + favorites = userFavorites; + } + + /** + * @return the id + */ + public long getId() { + return id; + } + + /** + * @return the dataGridId + */ + public long getDataGridId() { + return dataGridId; + } + + /** + * @return the username + */ + public String getUsername() { + return username; + } + + /** + * @return the additionalInfo + */ + public String getAdditionalInfo() { + return additionalInfo; + } + + /** + * @return the enabled + */ + public boolean isEnabled() { + return enabled; + } + + /** + * @param id + * the id to set + */ + public void setId(long id) { + this.id = id; + } + + /** + * @param dataGridId + * the dataGridId to set + */ + public void setDataGridId(long dataGridId) { + this.dataGridId = dataGridId; + } + + /** + * @param username + * the username to set + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * @param additionalInfo + * the additionalInfo to set + */ + public void setAdditionalInfo(String additionalInfo) { + this.additionalInfo = additionalInfo; + } + + /** + * @param enabled + * the enabled to set + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * @return the firstName + */ + public String getFirstName() { + return firstName; + } + + /** + * @return the lastName + */ + public String getLastName() { + return lastName; + } + + /** + * @param firstName + * the firstName to set + */ + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + /** + * @param lastName + * the lastName to set + */ + public void setLastName(String lastName) { + this.lastName = lastName; + } + + /** + * @return the email + */ + public String getEmail() { + return email; + } + + /** + * @param email + * the email to set + */ + public void setEmail(String email) { + this.email = email; + } + + /** + * @return the userProfile + */ + public UserProfile getUserProfile() { + return userProfile; + } + + /** + * @param userProfile + * the userProfile to set + */ + public void setUserProfile(UserProfile userProfile) { + this.userProfile = userProfile; + } + + /** + * @return the locale + */ + public String getLocale() { + return locale; + } + + /** + * @param locale + * the locale to set + */ + public void setLocale(String locale) { + this.locale = locale; + } + + public String getUserType() { + return userType; + } + + public void setUserType(String userType) { + this.userType = userType; + } + + /** + * @return the organizationalRole + */ + public String getOrganizationalRole() { + return organizationalRole; + } + + /** + * @param organizationalRole + * the organizationalRole to set + */ + public void setOrganizationalRole(String organizationalRole) { + this.organizationalRole = organizationalRole; + } + + /** + * @return the company + */ + public String getCompany() { + return company; + } + + /** + * @return the department + */ + public String getDepartment() { + return department; + } + + /** + * @return the title + */ + public String getTitle() { + return title; + } + + /** + * @param company + * the company to set + */ + public void setCompany(String company) { + this.company = company; + } + + /** + * @param department + * the department to set + */ + public void setDepartment(String department) { + this.department = department; + } + + /** + * @param title + * the title to set + */ + public void setTitle(String title) { + this.title = title; + } + + @Override + public int compareTo(DataGridUser o) { + return username.compareTo(o.getUsername()); + } + + public boolean isAdmin() { + return userType.compareTo("rodsadmin") == 0; + } + + /** + * @return the forceFileOverwriting + */ + public boolean isForceFileOverwriting() { + return forceFileOverwriting; + } + + /** + * @param forceFileOverwriting + * the forceFileOverwriting to set + */ + public void setForceFileOverwriting(boolean forceFileOverwriting) { + this.forceFileOverwriting = forceFileOverwriting; + } + + /** + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * @param password + * the password to set + */ + public void setPassword(String password) { + this.password = password; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUserBookmark.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUserBookmark.java new file mode 100755 index 000000000..7ed6f68b3 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUserBookmark.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.utils.DataGridCoreUtils; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +import javax.persistence.*; +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.Date; + +@Entity +@Audited +@Table(name = "user_bookmarks") +public class DataGridUserBookmark implements Serializable, Comparable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @NotAudited + @Column(name = "id", unique = true, nullable = false) + private Long id; + + @ManyToOne(fetch = FetchType.EAGER, optional = true) + @JoinColumn(name = "user_id", nullable = false, updatable = true) + private DataGridUser user; + + @Column(name = "path", nullable = false, length = 512) + private String path; + + @Column(name = "name", nullable = false, length = 512) + private String name; + + @Column(name = "is_notified", nullable = true) + private Boolean isNotified; + + @Column(name = "is_collection", nullable = false) + private Boolean isCollection; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_at", nullable = false, length = 60, updatable = false) + private Date createTs; + + private static final long serialVersionUID = -229875209906357557L; + + /** + * @return the id + */ + public Long getId() { + return id; + } + + /** + * @return the user + */ + @JsonIgnore + public DataGridUser getUser() { + return user; + } + + /** + * @return the path + */ + public String getPath() { + return path; + } + + /** + * @return the isNotified + */ + public Boolean getIsNotified() { + return isNotified; + } + + /** + * @return the createTs + */ + public Date getCreateTs() { + return createTs; + } + + public String getCreateTsFormatted() { + return new SimpleDateFormat("MMM dd yyyy, HH:mm").format(createTs); + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name + * the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the isCollection + */ + public Boolean getIsCollection() { + return isCollection; + } + + /** + * @param isCollection + * the isCollection to set + */ + public void setIsCollection(Boolean isCollection) { + this.isCollection = isCollection; + } + + /** + * @param id + * the id to set + */ + public void setId(Long id) { + this.id = id; + } + + /** + * @param user + * the user to set + */ + public void setUser(DataGridUser user) { + this.user = user; + } + + /** + * @param path + * the path to set + */ + public void setPath(String path) { + this.path = path; + } + + /** + * @param isNotified + * the isNotified to set + */ + public void setIsNotified(Boolean isNotified) { + this.isNotified = isNotified; + } + + /** + * @param createTs + * the createTs to set + */ + public void setCreateTs(Date createTs) { + this.createTs = createTs; + } + + /** + * Gets the icon to be displayed for a bookmarks based on its extension + * + * @return String containing the icon name to be displayed + */ + public String getDisplayIcon() { + return DataGridCoreUtils.getIconToDisplay(this.getPath()); + } + + @Override + public int compareTo(DataGridUserBookmark dgub) { + return this.getName().compareTo(dgub.getName()); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUserFavorite.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUserFavorite.java new file mode 100755 index 000000000..bc7aca9b2 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUserFavorite.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import com.emc.metalnx.core.domain.utils.DataGridCoreUtils; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +import javax.persistence.*; +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.Date; + +@Entity +@Audited +@Table(name = "user_favorites", uniqueConstraints = @UniqueConstraint(columnNames = { "user_id", + "path_hash" })) +public class DataGridUserFavorite implements Serializable, Comparable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @NotAudited + @Column(name = "id", unique = true, nullable = false) + private Long id; + + @ManyToOne(fetch = FetchType.EAGER, optional = true) + @JoinColumn(name = "user_id", nullable = false, updatable = true) + private DataGridUser user; + + @Column(name = "path", nullable = false, length = 512) + private String path; + + @Column(name = "name", nullable = false, length = 512) + private String name; + + @Column(name = "is_collection", nullable = true) + private Boolean isCollection; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_at", nullable = false, length = 60, updatable = false) + private Date createTs; + + @Column(name = "path_hash", nullable = false) + private int pathHash; + + private static final long serialVersionUID = -7923823760209937080L; + + /** + * @return the id + */ + @JsonIgnore + public Long getId() { + return id; + } + + /** + * @return the user + */ + @JsonIgnore + public DataGridUser getUser() { + return user; + } + + /** + * @return the path + */ + public String getPath() { + return path; + } + + /** + * @return the isCollection + */ + public boolean getIsCollection() { + return isCollection; + } + + /** + * @return the createTs + */ + @JsonIgnore + public Date getCreateTs() { + return createTs; + } + + public String getCreateTsFormatted() { + return new SimpleDateFormat("MMM dd yyyy, HH:mm").format(createTs); + } + + /** + * @param id + * the id to set + */ + public void setId(Long id) { + this.id = id; + } + + /** + * @param user + * the user to set + */ + public void setUser(DataGridUser user) { + this.user = user; + } + + /** + * @param path + * the path to set + */ + public void setPath(String path) { + this.path = path; + } + + /** + * @param fileName + * the fileName to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @param isNotified + * the isCollection to set + */ + public void setIsCollection(Boolean isCollection) { + this.isCollection = isCollection; + } + + /** + * @param createTs + * the createTs to set + */ + public void setCreateTs(Date createTs) { + this.createTs = createTs; + } + + /** + * Finds the file name based on its path + * + * @return file name + */ + public String getName() { + return this.name; + } + + /** + * @return the pathHash + */ + public int getPathHash() { + return pathHash; + } + + /** + * @param pathHash + * the pathHash to set + */ + public void setPathHash(int pathHash) { + this.pathHash = pathHash; + } + + /** + * Gets the icon to be displayed for a bookmarks based on its extension + * + * @return String containing the icon name to be displayed + */ + public String getDisplayIcon() { + return DataGridCoreUtils.getIconToDisplay(this.getPath()); + } + + @Override + public int compareTo(DataGridUserFavorite dgub) { + return this.getName().compareTo(dgub.getName()); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUserPermission.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUserPermission.java new file mode 100755 index 000000000..cd88d59ca --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridUserPermission.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +public class DataGridUserPermission { + + private Integer dataGridId; + private String userName; + private String userSystemRole; + private String permission; + + /** + * @return the dataGridId + */ + public Integer getDataGridId() { + return dataGridId; + } + + /** + * @return the userName + */ + public String getUserName() { + return userName; + } + + /** + * @return the permission + */ + public String getPermission() { + return permission; + } + + /** + * @param dataGridId + * the dataGridId to set + */ + public void setDataGridId(Integer dataGridId) { + this.dataGridId = dataGridId; + } + + /** + * @param userName + * the userName to set + */ + public void setUserName(String userName) { + this.userName = userName; + } + + /** + * @param permission + * the permission to set + */ + public void setPermission(String permission) { + this.permission = permission; + } + + /** + * @return the userSystemRole + */ + public String getUserSystemRole() { + return userSystemRole; + } + + /** + * @param userSystemRole + * the userSystemRole to set + */ + public void setUserSystemRole(String userSystemRole) { + this.userSystemRole = userSystemRole; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridZone.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridZone.java new file mode 100755 index 000000000..3b6012796 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/DataGridZone.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import java.util.Date; + +/** + * Represents an abstraction of a Zone in a data grid system + * @author guerra + * + */ +public class DataGridZone { + + private long id; + private String name; + private String type; + private String connectionString; + private String comment; + private Date createTime; + private Date modifyTime; + + public DataGridZone() { + //empty constructor + } + + public DataGridZone(String name, String type) { + this.name = name; + this.type = type; + } + + /** + * @return the id + */ + public long getId() { + return id; + } + /** + * @param id the id to set + */ + public void setId(long id) { + this.id = id; + } + /** + * @return the name + */ + public String getName() { + return name; + } + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + /** + * @return the type + */ + public String getType() { + return type; + } + /** + * @param type the type to set + */ + public void setType(String type) { + this.type = type; + } + /** + * @return the connectionString + */ + public String getConnectionString() { + return connectionString; + } + /** + * @param connectionString the connectionString to set + */ + public void setConnectionString(String connectionString) { + this.connectionString = connectionString; + } + /** + * @return the comment + */ + public String getComment() { + return comment; + } + /** + * @param comment the comment to set + */ + public void setComment(String comment) { + this.comment = comment; + } + /** + * @return the createTime + */ + public Date getCreateTime() { + return createTime; + } + /** + * @param createTime the createTime to set + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + /** + * @return the modifyTime + */ + public Date getModifyTime() { + return modifyTime; + } + /** + * @param modifyTime the modifyTime to set + */ + public void setModifyTime(Date modifyTime) { + this.modifyTime = modifyTime; + } + + + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/UserProfile.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/UserProfile.java new file mode 100755 index 000000000..6b1b887b6 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/UserProfile.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; + +import javax.persistence.*; +import java.util.Set; + +@Entity +@Audited +@Table(name = "user_profile") +public class UserProfile { + + @Id + @Column(name="id") + @NotAudited + @GeneratedValue + private Long profileId; + + @Column(name = "profile_name", nullable = false, length = 64) + private String profileName; + + @Column(name = "description", nullable = false, length = 512) + private String description; + + @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST}) + @JoinTable(name="user_profile_groups", joinColumns={@JoinColumn(name="profile_id")}, inverseJoinColumns={@JoinColumn(name="group_id")}) + private Set groups; + + @OneToMany(mappedBy = "userProfile", fetch = FetchType.EAGER) + private Set users; + + public UserProfile() { + + } + + public UserProfile(String profileName, String description) { + this.profileName = profileName; + this.description = description; + } + + public UserProfile(String profileName, String description, Set groups, Set users) { + this.profileName = profileName; + this.description = description; + this.groups = groups; + this.users = users; + } + + /** + * @return the profileId + */ + public Long getProfileId() { + return profileId; + } + + /** + * @param profileId the profileId to set + */ + public void setProfileId(Long profileId) { + this.profileId = profileId; + } + + /** + * @return the profileName + */ + public String getProfileName() { + return profileName; + } + + /** + * @param profileName the profileName to set + */ + public void setProfileName(String profileName) { + this.profileName = profileName; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @return the groups + */ + public Set getGroups() { + return groups; + } + + /** + * @param groups the groups to set + */ + public void setGroups(Set groups) { + this.groups = groups; + } + + /** + * @return the users + */ + public Set getUsers() { + return users; + } + + /** + * @param users the users to set + */ + public void setUsers(Set users) { + this.users = users; + } + + public String toString() { + return this.profileName; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridPermType.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridPermType.java new file mode 100755 index 000000000..433d6b5db --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridPermType.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity.enums; + +/* + * Defines all permission types that exist in the data grid. + * */ + +public enum DataGridPermType { + + OWN("OWN"), WRITE("WRITE"), READ("READ"), IRODS_ADMIN("ADMIN"), NONE("NONE"); + + private String stringType; + + DataGridPermType(String type) { + this.stringType = type.toUpperCase(); + } + + @Override + public String toString() { + return this.stringType; + } + +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridResourceTypeEnum.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridResourceTypeEnum.java new file mode 100755 index 000000000..736a08a78 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridResourceTypeEnum.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity.enums; + +public enum DataGridResourceTypeEnum { + + IRODS_COORDINATING("COORDINATING"), IRODS_STORAGE("STORAGE"); + + private String stringType; + + DataGridResourceTypeEnum(String type) { + this.stringType = type; + } + + @Override + public String toString() { + return this.stringType; + } + +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridSearchOperatorEnum.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridSearchOperatorEnum.java new file mode 100755 index 000000000..3545609d8 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridSearchOperatorEnum.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity.enums; + +public enum DataGridSearchOperatorEnum { + + EQUAL("="), + NOT_EQUAL("!="), + LIKE("ILIKE"), + NOT_LIKE("NOT ILIKE"), + BIGGER_THAN(">"), + LESS_THAN("<"); + + private String stringType; + + private DataGridSearchOperatorEnum(String type) { + this.stringType = type; + } + + public String toString() { + return this.stringType; + } + +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridServerType.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridServerType.java new file mode 100755 index 000000000..1b2fc3c2a --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/DataGridServerType.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity.enums; + +public enum DataGridServerType { + + ICAT_SERVER("ICAT_SERVER"), + RESOURCE_SERVER("RESOURCE_SERVER"), + ISILON("ISILON"); + + private String stringType; + + private DataGridServerType(String type) { + this.stringType = type; + } + + public String toString() { + return this.stringType; + } + +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/FilePropertyField.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/FilePropertyField.java new file mode 100755 index 000000000..20cfe5d3f --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/FilePropertyField.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity.enums; + +public enum FilePropertyField { + + FILE_NAME("name"), + FILE_PATH("path"), + RESC_NAME("resc_name"), + OWNER_NAME("owner_name"), + CREATION_DATE("create_ts"), + MODIFICATION_DATE("modify_ts"), + SIZE("size"), + REPLICA_NUMBER("repl_num"), + CHECKSUM("checksum"), + COMMENT("r_comment"); + + private String fieldName; + + FilePropertyField(String fieldName) { + this.fieldName = fieldName; + } + + /** + * Finds the correct label associated to the input String + * @param fieldName field name as is written in front-end + * @return field name as is used in database + */ + public static FilePropertyField findByString(String fieldName) { + + if (fieldName == null) { + return null; + } + + for(FilePropertyField field : FilePropertyField.values()) { + if (field.getFieldName().compareTo(fieldName) == 0) { + return field; + } + } + return null; + } + + /** + * @return the fieldName + */ + public String getFieldName() { + return fieldName; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/FilePropertySearchOperator.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/FilePropertySearchOperator.java new file mode 100755 index 000000000..087765cdb --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/entity/enums/FilePropertySearchOperator.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.entity.enums; + +public enum FilePropertySearchOperator { + + EQUALS("="), + NOT_EQUALS("!="), + CONTAINS("ILIKE"), + NOT_CONTAINS("NOT ILIKE"); + + private String operator; + + FilePropertySearchOperator(String operator) { + this.operator = operator; + } + + /** + * Finds the correct label associated to the input String + * @param accessType + * @return + */ + public static FilePropertySearchOperator findByString(String operatorStr) { + + if (operatorStr == null) { + return null; + } + + for(FilePropertySearchOperator operator : FilePropertySearchOperator.values()) { + if (operator.getFieldName().compareTo(operatorStr) == 0) { + return operator; + } + } + return null; + } + + /** + * @return the fieldName + */ + public String getFieldName() { + return this.operator; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridAuthenticationException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridAuthenticationException.java new file mode 100755 index 000000000..bf7c8c6ab --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridAuthenticationException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +import org.springframework.security.core.AuthenticationException; + +public class DataGridAuthenticationException extends AuthenticationException { + private static final long serialVersionUID = 1L; + + public DataGridAuthenticationException(String msg) { + super(msg); + } + + public DataGridAuthenticationException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridChecksumException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridChecksumException.java new file mode 100755 index 000000000..3b8a0133c --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridChecksumException.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception used when no checksum can be calculated on a file in the grid. + */ +public class DataGridChecksumException extends DataGridException { + public DataGridChecksumException(String msg) { + super(msg); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridConnectionRefusedException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridConnectionRefusedException.java new file mode 100755 index 000000000..2e3786899 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridConnectionRefusedException.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +public class DataGridConnectionRefusedException extends DataGridException { + + private static final long serialVersionUID = 1L; + + public DataGridConnectionRefusedException() { + super(); + } + + public DataGridConnectionRefusedException(String message) { + super(message); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridCorruptedFileException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridCorruptedFileException.java new file mode 100755 index 000000000..9e7fca903 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridCorruptedFileException.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +public class DataGridCorruptedFileException extends DataGridException { + private static final long serialVersionUID = 1L; + + public DataGridCorruptedFileException(String msg) { + super(msg); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridDataNotFoundException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridDataNotFoundException.java new file mode 100755 index 000000000..86dc95bbe --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridDataNotFoundException.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +public class DataGridDataNotFoundException extends Exception { + private static final long serialVersionUID = 1L; + + public DataGridDataNotFoundException() { + super(); + } + + public DataGridDataNotFoundException(String msg) { + super(msg); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridDatabaseException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridDatabaseException.java new file mode 100755 index 000000000..6b456066f --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridDatabaseException.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +import org.springframework.security.core.AuthenticationException; + +public class DataGridDatabaseException extends AuthenticationException { + public DataGridDatabaseException(String message) { + super(message); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridException.java new file mode 100755 index 000000000..b6ae2c05d --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +public class DataGridException extends Exception { + + private static final long serialVersionUID = 1L; + + public DataGridException() { + super(); + } + + public DataGridException(String msg) { + super(msg); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridFileAlreadyExistsException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridFileAlreadyExistsException.java new file mode 100755 index 000000000..b3fb5174a --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridFileAlreadyExistsException.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception classes used when a file is moved, copied, uploaded to a collection, but it already exists there. + */ +public class DataGridFileAlreadyExistsException extends DataGridException { + public DataGridFileAlreadyExistsException(String message) { + super(message); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridInvalidPathException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridInvalidPathException.java new file mode 100755 index 000000000..b1644d4dd --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridInvalidPathException.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +public class DataGridInvalidPathException extends DataGridException { + + private static final long serialVersionUID = 1L; + + public DataGridInvalidPathException() { + super(); + } + + public DataGridInvalidPathException(String message) { + super(message); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridMSIVersionNotSupported.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridMSIVersionNotSupported.java new file mode 100755 index 000000000..f35c78e00 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridMSIVersionNotSupported.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when the MSI package installed is not supported by this application. + */ +public class DataGridMSIVersionNotSupported extends DataGridException { + public DataGridMSIVersionNotSupported(String msg) { super(msg); } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridQueryException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridQueryException.java new file mode 100755 index 000000000..bb0c98be2 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridQueryException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +public class DataGridQueryException extends Exception { + + private static final long serialVersionUID = 1L; + + public DataGridQueryException() { + super(); + } + + public DataGridQueryException(String msg) { + super(msg); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridReplicateException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridReplicateException.java new file mode 100755 index 000000000..b5a87f68f --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridReplicateException.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when a replication of a file fails. + */ +public class DataGridReplicateException extends DataGridException { + public DataGridReplicateException(String msg) { + super(msg); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridRuleException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridRuleException.java new file mode 100755 index 000000000..3c1bc0530 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridRuleException.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when the data grid cannot execute a rule. + */ +public class DataGridRuleException extends DataGridException { + + public DataGridRuleException(String msg) { + super(msg); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridServerException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridServerException.java new file mode 100755 index 000000000..233738654 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridServerException.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +import org.springframework.security.core.AuthenticationException; + +public class DataGridServerException extends AuthenticationException { + public DataGridServerException(String message) { + super(message); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTemplateAttrException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTemplateAttrException.java new file mode 100755 index 000000000..526a92c99 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTemplateAttrException.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when an attribute field of a template exceed the limit available in the database. + * + */ +public class DataGridTemplateAttrException extends DataGridException { + private static final long serialVersionUID = 1L; + + public DataGridTemplateAttrException() { + super(); + } + + public DataGridTemplateAttrException(String msg) { + super(msg); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTemplateUnitException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTemplateUnitException.java new file mode 100755 index 000000000..7f0525383 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTemplateUnitException.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when an unit field of a template exceed the limit available in the database. + * + */ +public class DataGridTemplateUnitException extends DataGridException { + private static final long serialVersionUID = 1L; + + public DataGridTemplateUnitException() { + super(); + } + + public DataGridTemplateUnitException(String msg) { + super(msg); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTemplateValueException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTemplateValueException.java new file mode 100755 index 000000000..dbe735f44 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTemplateValueException.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when a value field of a template exceed the limit available in the database. + * + */ +public class DataGridTemplateValueException extends DataGridException { + private static final long serialVersionUID = 1L; + + public DataGridTemplateValueException() { + super(); + } + + public DataGridTemplateValueException(String msg) { + super(msg); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketDownloadException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketDownloadException.java new file mode 100755 index 000000000..2c73e1f05 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketDownloadException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when there is an error during download with tickets. + */ +public class DataGridTicketDownloadException extends DataGridException { + private String path, ticketString; + + public DataGridTicketDownloadException(String msg, String path, String ticketString) { + super(msg); + this.path = path; + this.ticketString = ticketString; + } + + public String getPath() { + return path; + } + + public String getTicketString() { + return ticketString; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketException.java new file mode 100755 index 000000000..6d13971ca --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketException.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when an error occurs during any ticket operation + */ +public class DataGridTicketException extends DataGridException { + public DataGridTicketException(String msg) { super(msg); } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketFileNotFoundException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketFileNotFoundException.java new file mode 100755 index 000000000..ce88e5905 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketFileNotFoundException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +public class DataGridTicketFileNotFoundException extends DataGridException { + + private static final long serialVersionUID = 1L; + + private String path, ticketString; + + public DataGridTicketFileNotFoundException(String msg, String path, String ticketString) { + super(msg); + this.path = path; + this.ticketString = ticketString; + } + + public String getPath() { + return path; + } + + public String getTicketString() { + return ticketString; + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketInvalidUserException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketInvalidUserException.java new file mode 100755 index 000000000..ca5b58bf9 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketInvalidUserException.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when a not-authenticated user (anonymous) tries to access a file/collection via ticket. + */ +public class DataGridTicketInvalidUserException extends DataGridException { + public DataGridTicketInvalidUserException(String message) { + super(message); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketNotFoundException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketNotFoundException.java new file mode 100755 index 000000000..e8f6f34c2 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketNotFoundException.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when ticket cannot be found. + */ +public class DataGridTicketNotFoundException extends DataGridException { + public DataGridTicketNotFoundException(String message) { + super(message); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketUploadException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketUploadException.java new file mode 100755 index 000000000..27d7a65f4 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTicketUploadException.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Class that represents the error during an upload with a ticket + */ +public class DataGridTicketUploadException extends DataGridException { + public DataGridTicketUploadException(String message) { + super(message); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTooLongTemplateNameException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTooLongTemplateNameException.java new file mode 100755 index 000000000..42779d264 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/DataGridTooLongTemplateNameException.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.exceptions; + +/** + * Exception thrown when the name of a template exceed the limit available in the database. + * + */ +public class DataGridTooLongTemplateNameException extends DataGridException { + private static final long serialVersionUID = 1L; + + public DataGridTooLongTemplateNameException() { + super(); + } + + public DataGridTooLongTemplateNameException(String msg) { + super(msg); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/UnsupportedDataGridFeatureException.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/UnsupportedDataGridFeatureException.java new file mode 100644 index 000000000..74c0e593e --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/exceptions/UnsupportedDataGridFeatureException.java @@ -0,0 +1,30 @@ +/** + * + */ +package com.emc.metalnx.core.domain.exceptions; + +/** + * Signals an attempt at an operation not supported on the target data grid (due + * to version difference, plugin availability difference etc. + * + * @author Mike Conway - NIEHS + * + */ +public class UnsupportedDataGridFeatureException extends DataGridException { + + private static final long serialVersionUID = 386934177608694036L; + + /** + * + */ + public UnsupportedDataGridFeatureException() { + } + + /** + * @param msg + */ + public UnsupportedDataGridFeatureException(String msg) { + super(msg); + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/utils/DataGridCoreUtils.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/utils/DataGridCoreUtils.java new file mode 100755 index 000000000..922b8cf8e --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/utils/DataGridCoreUtils.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.utils; + +import java.util.*; + +/** + * Helper class for common operations with core classes. + * + */ +public class DataGridCoreUtils { + + public static final String MSI_LIST_SEPARATOR = ","; + + /** + * Finds the MSI API version currently supported. + * @return String containing the MSI API version + */ + public static String getAPIVersion(String version) { + if(version == null || version.isEmpty()) return ""; + + int end = version.indexOf('.'); + + if (end < 0) end = version.length(); + + return version.substring(0, end); + } + + public static boolean isIllumina(String path) { + return path.endsWith("_SSU.tar"); + } + + /** + * Checks whether or not a given path refers to a BAM or CRAM file. + * @param path file path + * @return True, if file is BAM/CRAM. False, otherwise. + */ + public static boolean isBamOrCram(String path) { + return path.endsWith(".cram") || path.endsWith(".bam"); + } + + /** + * Auxiliary method to determine wether a file is an image file + * + * @param path file path + * @return bool True, if the given path is an image. False, otherwise. + */ + public static boolean isImageFile(String path) { + Set extensions = new HashSet<>(); + extensions.add("png"); + extensions.add("PNG"); + extensions.add("jpg"); + extensions.add("JPG"); + extensions.add("jpeg"); + extensions.add("JPEG"); + extensions.add("bmp"); + extensions.add("BMP"); + + String fileExtension = ""; + + int i = path.lastIndexOf('.'); + if (i > 0) fileExtension = path.substring(i + 1); + + return extensions.contains(fileExtension); + } + + /** + * Auxiliary method to determine wether a file is a VCF file + * + * @param path file path + * @return bool True, if the given path is a VCF file. False, otherwise. + */ + public static boolean isVCFFile(String path) { + Set extensions = new HashSet<>(); + extensions.add("vcf"); + extensions.add("VCF"); + + String fileExtension = ""; + + int i = path.lastIndexOf('.'); + if (i > 0) { + fileExtension = path.substring(i + 1); + } + + return extensions.contains(fileExtension); + } + + /** + * Auxiliary method to determine whether a file is a VCF file + * + * @param path file path + * @return bool True, if the given path is a manifest file. False, otherwise. + */ + public static boolean isPrideXMLManifestFile(String path) { + Set extensions = new HashSet<>(); + extensions.add("xml"); + + String fileExtension = ""; + + int i = path.lastIndexOf('.'); + if (i > 0) { + fileExtension = path.substring(i + 1); + } + + return extensions.contains(fileExtension); + } + + /** + * Gets the icon type that will be shown on the UI. + * + * @param filePath path to the file + * @return the icon type as String + */ + public static String getIconToDisplay(String filePath) { + + String icon; + String extension = getFileExtension(filePath); + + switch (extension) { + + case "jpg": + case "jpeg": + case "png": + case "gif": + icon = "fa fa-file-image-o"; + break; + + case "html": + case "htm": + case "xml": + case "tex": + icon = "fa fa-code"; + break; + + case "c": + case "cpp": + case "java": + case "py": + icon = "fa fa-file-code-o"; + break; + + case "docx": + case "doc": + icon = "fa fa-file-word-o"; + break; + + case "xlsx": + case "xls": + icon = "fa fa-file-excel-o"; + break; + + case "pptx": + case "ppt": + icon = "fa fa-file-powerpoint-o"; + break; + + case "pdf": + icon = "fa fa-file-pdf-o"; + break; + + case "zip": + case "rar": + icon = "fa fa-file-archive-o"; + break; + + case "unknown": + icon = "fa fa-folder folder-icon"; + break; + + default: + icon = "fa fa-file"; + break; + } + + return icon; + + } + + /** + * Gets file extension based on its path + * + * @param filePath path to the file + * @return file extension + */ + private static String getFileExtension(String filePath) { + + if (filePath.lastIndexOf(".") != -1 && filePath.lastIndexOf(".") != 0) { + return filePath.substring(filePath.lastIndexOf(".") + 1); + } + + else + return "unknown"; + + } + + /** + * Parses a raw list of MSIs coming from a rule. + * @param msisAsString list of MSIs coming from the rule as a string + * @return List of MSIs + */ + public static List getMSIsAsList(String msisAsString) { + List msis = new ArrayList<>(); + + if(msisAsString != null && !msisAsString.isEmpty()) { + for (String msi: msisAsString.split(MSI_LIST_SEPARATOR)) { + String msiName = msi.trim(); + if (!msiName.isEmpty()) msis.add(msiName); + } + } + + return msis; + } + + public static void fillMSIMap(List msis, Map map) { + if(msis == null || msis.isEmpty()) return; + + for(String msi: msis) if(!msi.isEmpty()) map.put(msi, false); + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/utils/DataGridJsonDateDeserializer.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/utils/DataGridJsonDateDeserializer.java new file mode 100755 index 000000000..528be95cd --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/utils/DataGridJsonDateDeserializer.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.utils; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.text.ParseException; +import java.util.Date; + +import static com.emc.metalnx.core.domain.entity.DataGridTicket.dateFormat; + +/** + * Custom deserializer for dates as a JSON. + */ +public class DataGridJsonDateDeserializer extends JsonDeserializer { + @Override + public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + String date = jsonParser.getText(); + + if (date == null || date.isEmpty()) { + return null; + } + + try { + return dateFormat.parse(date); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } +} diff --git a/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/utils/DataGridTemplateFieldComparator.java b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/utils/DataGridTemplateFieldComparator.java new file mode 100755 index 000000000..772f31200 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/java/com/emc/metalnx/core/domain/utils/DataGridTemplateFieldComparator.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.core.domain.utils; + +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; + +import java.util.Comparator; + +public class DataGridTemplateFieldComparator implements Comparator { + + @Override + public int compare(DataGridTemplateField o1, DataGridTemplateField o2) { + if (o1.getAttribute().compareTo(o2.getAttribute()) != 0) { + return o1.getAttribute().compareTo(o2.getAttribute()); + } + + else if (o1.getValue().compareTo(o2.getValue()) != 0) { + return o1.getValue().compareTo(o2.getValue()); + } + + else if (o1.getUnit().compareTo(o2.getUnit()) != 0) { + return o1.getUnit().compareTo(o2.getUnit()); + } + + return 0; + } + +} diff --git a/packaging/src/emc-metalnx-core/src/main/resources/META-INF/emc-metalnx-core/emc-metalnx-core-context.xml b/packaging/src/emc-metalnx-core/src/main/resources/META-INF/emc-metalnx-core/emc-metalnx-core-context.xml new file mode 100755 index 000000000..e1e2c13f5 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/resources/META-INF/emc-metalnx-core/emc-metalnx-core-context.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/main/resources/META-INF/emc-metalnx-core/emc-metalnx-core-jpa.xml b/packaging/src/emc-metalnx-core/src/main/resources/META-INF/emc-metalnx-core/emc-metalnx-core-jpa.xml new file mode 100755 index 000000000..249f38a76 --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/resources/META-INF/emc-metalnx-core/emc-metalnx-core-jpa.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + ${hibernate.dialect} + ${hibernate.format_sql} + ${hibernate.show_sql} + ${hibernate.hbm2ddl.auto} + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/main/xsd/global.xjb b/packaging/src/emc-metalnx-core/src/main/xsd/global.xjb new file mode 100755 index 000000000..c35f5a1fe --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/xsd/global.xjb @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/main/xsd/mlx.metadata.template.001.xsd b/packaging/src/emc-metalnx-core/src/main/xsd/mlx.metadata.template.001.xsd new file mode 100755 index 000000000..8433a992d --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/main/xsd/mlx.metadata.template.001.xsd @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-core/src/test/resources/META-INF/MANIFEST.MF b/packaging/src/emc-metalnx-core/src/test/resources/META-INF/MANIFEST.MF new file mode 100644 index 000000000..254272e1c --- /dev/null +++ b/packaging/src/emc-metalnx-core/src/test/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/packaging/src/emc-metalnx-services/pom.xml b/packaging/src/emc-metalnx-services/pom.xml new file mode 100755 index 000000000..cc69b78ea --- /dev/null +++ b/packaging/src/emc-metalnx-services/pom.xml @@ -0,0 +1,259 @@ + + + + 4.0.0 + + com.emc.metalnx + emc-metalnx + 1.4.0 + + emc-metalnx-services + + + org.springframework + spring-context + + + com.emc.metalnx + emc-metalnx-core + ${project.version} + + + org.irods.jargon + jargon-core + + + org.irods.jargon + jargon-ruleservice + + + org.irods.jargon + jargon-user-tagging + + + org.irods.jargon + jargon-ticket + + + javax.servlet + javax.servlet-api + + + org.springframework + spring-test + + + + junit + junit + test + + + + javax.el + el-api + test + + + + org.mockito + mockito-core + test + + + + + + + + maven-antrun-plugin + + + + 0 + validate + + + + + + test.data.directory=${jargon.test.data.directory} + test.irods.admin=${jargon.test.irods.admin} + test.irods.admin.password=${jargon.test.irods.admin.password} + test.irods.user=${jargon.test.irods.user} + test.irods.password=${jargon.test.irods.password} + test.irods.resource=${jargon.test.irods.resource} + test2.irods.user=${jargon.test.irods.user2} + test2.irods.password=${jargon.test.irods.password2} + test2.irods.resource=${jargon.test.irods.resource2} + test3.irods.user=${jargon.test.irods.user3} + test3.irods.password=${jargon.test.irods.password3} + test3.irods.resource=${jargon.test.irods.resource3} + test.irods.host=${jargon.test.irods.host} + test.resource.host=${jargon.test.resource.host} + test.irods.port=${jargon.test.irods.port} + test.irods.zone=${jargon.test.irods.zone} + jargon.test.kerberos.user=${jargon.test.kerberos.user} + jargon.test.user.group=${jargon.test.user.group} + test.resource.group=${jargon.test.resource.group} + test.irods.userDN=${jargon.test.irods.userDN} + test.irods.scratch.subdir=${jargon.test.irods.scratch.subdir} + test.option.exercise.remoteexecstream=${jargon.test.option.exercise.remoteexecstream} + test.option.eirods=${test.option.eirods} + test.option.exercise.audit=${jargon.test.option.exercise.audit} + test.option.exercise.workflow=${jargon.test.option.exercise.workflow} + test.option.exercise.filesystem.mount=${jargon.test.option.exercise.filesystem.mount} + test.option.exercise.filesystem.mount.local=${jargon.test.option.exercise.filesystem.mount.local} + test.option.distributed.resources=${test.option.distributed.resources} + test.option.registration=${test.option.registration} + test.option.strictACL=${test.option.strictACL} + test.option.federated.zone=${test.option.federated.zone} + test.option.kerberos=${test.option.kerberos} + test.option.pam=${test.option.pam} + test.option.ssl.configured=${test.option.ssl.configured} + jargon.test.pam.user=${jargon.test.pam.user} + jargon.test.pam.password=${jargon.test.pam.password} + test.federated.irods.admin=${jargon.test.federated.irods.admin} + test.federated.irods.admin.password=${jargon.test.federated.irods.admin.password} + test.federated.irods.user=${jargon.test.federated.irods.user} + test.federated.irods.password=${jargon.test.federated.irods.password} + test.federated.irods.resource=${jargon.test.federated.irods.resource} + test.federated.irods.host=${jargon.test.federated.irods.host} + test.federated.irods.port=${jargon.test.federated.irods.port} + test.federated.irods.zone=${jargon.test.federated.irods.zone} + test.option.gsi=${test.option.gsi} + test.option.gsi.host=${test.option.gsi.host} + test.option.gsi.port=${test.option.gsi.port} + test.option.gsi.zone=${test.option.gsi.zone} + test.option.gsi.dn=${test.option.gsi.dn} + test.option.gsi.user=${test.option.gsi.user} + test.option.gsi.file=${test.option.gsi.file} + test.option.mount.basedir=${test.option.mount.basedir} + test.option.python=${test.option.python} + + + + + run + + + + + 2 + validate + + + + + + irods.host=${jargon.test.irods.host} + irods.port=${jargon.test.irods.port} + irods.zoneName=${jargon.test.irods.zone} + irods.admin.user=${jargon.test.irods.admin} + irods.admin.password=${jargon.test.irods.admin.password} + + + irods.auth.scheme=${metalnx.auth.scheme} + default.storage.resource=${jargon.test.irods.resource} + ssl.negotiation.policy=${metalnx.ssl.policy} + + ########################################################## + + utilize.packing.streams=${metalnx.packing.streams} + + + compute.checksum=${metalnx.compute.checksum} + + ########################################################## + + db.driverClassName=${metalnx.jdbc.driver} + db.url=${metalnx.jdbc.url} + db.username=${metalnx.jdbc.user} + db.password=${metalnx.jdbc.password} + hibernate.dialect=${metalnx.jdbc.dialect} + + + hibernate.show_sql=true + hibernate.format_sql=false + + + hibernate.hbm2ddl.auto=update + + + connection.pool_size=5 + + ###################################### + + jobs.irods.username=${jargon.test.irods.admin} + jobs.irods.password=${jargon.test.irods.admin.password} + jobs.irods.auth.scheme=${metalnx.auth.scheme} + runSyncJobs=true + + + rmd.connection.timeout=500 + rmd.connection.port=8000 + + reverse.dns.lookup=false + + ###################################### + + populate.msi.enabled=false + illumina.msi.enabled=true + + msi.api.version=1.X.X + + msi.metalnx.list=libmsiget_illumina_meta.so,libmsiobjget_microservices.so,libmsiobjget_version.so,libmsiobjjpeg_extract.so,libmsiobjput_mdbam.so,libmsiobjput_mdbam.so,libmsiobjput_mdmanifest.so,libmsiobjput_mdvcf.so,libmsiobjput_populate.so + + msi.irods.list=libmsisync_to_archive.so,libmsi_update_unixfilesystem_resource_free_space.so,libmsiobjput_http.so,libmsiobjput_irods.so,libmsiobjget_irods.so,libmsiobjget_http.so,libmsiobjput_slink.so,libmsiobjget_slink.so + + msi.irods.42.list=libmsisync_to_archive.so,libmsi_update_unixfilesystem_resource_free_space.so + + msi.other.list= + + resource.location.images=/images/,classpath:static/images/ + resource.location.fonts=/fonts/,classpath:static/fonts/ + resource.location.css=/css/,classpath:static/css/ + resource.location.js=/js/,classpath:static/js/ + resource.location.i18=classpath:i18n/messages + resource.location.i18-users=classpath:i18n-users/messages + + + + + + + run + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.plugin.version} + + 0 + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/context/EncodedPropertiesConfigurer.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/context/EncodedPropertiesConfigurer.java new file mode 100755 index 000000000..e748b4462 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/context/EncodedPropertiesConfigurer.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.context; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; +import org.springframework.util.Base64Utils; + +import java.io.UnsupportedEncodingException; +import java.net.Inet4Address; +import java.net.UnknownHostException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +public class EncodedPropertiesConfigurer extends PropertyPlaceholderConfigurer { + + private static final String PROPERTIES_TO_BE_DECODED = "encoded.properties"; + private static final String DEFAULT_ENCODED_FIELDS = "db.password,jobs.irods.password"; + private static final String PWD_SALT = "!M3t4Lnx@1234"; + + private static final Logger logger = LoggerFactory.getLogger(EncodedPropertiesConfigurer.class); + + @Override + protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException { + + for (String prop : propertiesToBeDecoded(props)) { + logger.debug("Decoding property [{}]", prop); + String currentValue = props.getProperty(prop); + props.setProperty(prop, decodePassword(currentValue)); + } + + super.processProperties(beanFactory, props); + } + + /** + * Decodes an encoded password on properties placeholders + * + * @param currentValue the current password value + * @return {@link String} decoded password + */ + private String decodePassword(String currentValue) { + logger.debug("Decoding value [{}]", currentValue); + + String pwd = ""; + Integer key = 0; + + try { + key = getKey(); + + byte[] encodedBytes = Base64Utils.decodeFromString(currentValue); + for(byte b: encodedBytes) { + pwd += (char) ((b ^ key) & 0xFF); + } + + } catch (UnknownHostException e) { + logger.error("Could not get machine's hostname at start up."); + } catch (NoSuchAlgorithmException e) { + logger.error("Could not get a MD5 algorithm at start up."); + } catch (UnsupportedEncodingException e) { + logger.error("Encoding not supported (US_ASCII)."); + } + + logger.debug("Decoded value [{}]", pwd); + return pwd; + } + + private Integer getKey() throws UnknownHostException, + NoSuchAlgorithmException, UnsupportedEncodingException { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + + String hostname = Inet4Address.getLocalHost().getHostName(); + + // Using only FQDN + if (hostname.contains(".")) { + hostname = hostname.substring(0, hostname.indexOf(".")); + } + + String s = PWD_SALT + hostname; + + Integer key = 0; + byte[] digestedBytes = md5.digest(s.getBytes()); + for (byte b : digestedBytes) { + key += b & 0xFF; + } + + return key; + } + + /** + * Returns the name of the encoded properties on the configuration + * files + * + * @param props the properties hashtable + * @return {@link java.lang.reflect.Array} of {@link String} + */ + private List propertiesToBeDecoded(Properties props) { + logger.debug("Looking for properties to be decoded"); + String propertiesToBeDecodedRaw = props.getProperty(PROPERTIES_TO_BE_DECODED, DEFAULT_ENCODED_FIELDS); + + logger.debug("The following properties will be decoded: {}", propertiesToBeDecodedRaw); + List properties = new ArrayList<>(Arrays.asList(propertiesToBeDecodedRaw.split(","))); + + // Filtering out all the invalid property names + Set propertyNames = props.stringPropertyNames(); + ListIterator lit = properties.listIterator(); + + while (lit.hasNext()) { + String currentPropName = (String) lit.next(); + if (!propertyNames.contains(currentPropName)) { + logger.warn("Found invalid property [{}].", currentPropName); + lit.remove(); + } + } + + return properties; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/auth/IRODSAuthenticationProvider.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/auth/IRODSAuthenticationProvider.java new file mode 100755 index 000000000..a369e9955 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/auth/IRODSAuthenticationProvider.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.auth; + +import com.emc.metalnx.core.domain.dao.UserDao; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridAuthenticationException; +import com.emc.metalnx.core.domain.exceptions.DataGridDatabaseException; +import com.emc.metalnx.core.domain.exceptions.DataGridServerException; +import com.emc.metalnx.services.interfaces.AuthenticationProviderService; +import org.irods.jargon.core.connection.AuthScheme; +import org.irods.jargon.core.connection.IRODSAccount; +import org.irods.jargon.core.connection.auth.AuthResponse; +import org.irods.jargon.core.exception.InvalidUserException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.protovalues.UserTypeEnum; +import org.irods.jargon.core.pub.IRODSAccessObjectFactory; +import org.irods.jargon.core.pub.UserAO; +import org.irods.jargon.core.pub.domain.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.transaction.TransactionException; +import org.springframework.util.StringUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class IRODSAuthenticationProvider implements AuthenticationProviderService, Serializable { + private static final String IRODS_ANONYMOUS_ACCOUNT = "anonymous"; + + @Autowired + UserDao userDao; + + @Autowired + IRODSAccessObjectFactory irodsAccessObjectFactory; + + private String irodsHost; + private String irodsPort; + private String irodsZoneName; + + @Value("${irods.auth.scheme}") + private String irodsAuthScheme; + + // Instance variables to be set to UserTokenDetails instance. + private IRODSAccount irodsAccount; + + private DataGridUser user; + + private static final Logger logger = LoggerFactory.getLogger(IRODSAuthenticationProvider.class); + + private static final long serialVersionUID = -4984545776727334580L; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + + String username = authentication.getName(); + String password = authentication.getCredentials().toString(); + AuthResponse authResponse; + UsernamePasswordAuthenticationToken authObject; + + logger.debug("Setting username {} and password {}.", username, password); + + try { + authResponse = this.authenticateAgainstIRODS(username, password); + + // Settings iRODS account + this.irodsAccount = authResponse.getAuthenticatedIRODSAccount(); + + // Retrieving logging user + User irodsUser = new User(); + + try { + irodsUser = this.irodsAccessObjectFactory.getUserAO(this.irodsAccount).findByName(username); + } catch (JargonException e) { + logger.error("Could not find user: " + e.getMessage()); + } + + GrantedAuthority grantedAuth; + if (irodsUser.getUserType().equals(UserTypeEnum.RODS_ADMIN)) { + grantedAuth = new IRODSAdminGrantedAuthority(); + } else { + grantedAuth = new IRODSUserGrantedAuthority(); + } + + // Settings granted authorities + List grantedAuths = new ArrayList(); + grantedAuths.add(grantedAuth); + + // Returning authentication token with the access object factory injected + authObject = new UsernamePasswordAuthenticationToken(username, password, grantedAuths); + + // Creating UserTokenDetails instance for the current authenticated user + UserTokenDetails userDetails = new UserTokenDetails(); + userDetails.setIrodsAccount(this.irodsAccount); + userDetails.setUser(this.user); + + // Settings the user details object into the authentication object + authObject.setDetails(userDetails); + } catch (TransactionException e) { + logger.error("Database not responding"); + throw new DataGridDatabaseException(e.getMessage()); + } catch (InvalidUserException | org.irods.jargon.core.exception.AuthenticationException e) { + logger.error("Could not authenticate user: ", username); + throw new DataGridAuthenticationException(e.getMessage()); + } catch (JargonException e) { + logger.error("Server not responding"); + throw new DataGridServerException(e.getMessage()); + } + + return authObject; + } + + @Override + public boolean supports(Class authentication) { + return authentication.equals(UsernamePasswordAuthenticationToken.class); + } + + private AuthResponse authenticateAgainstIRODS(String username, String password) throws JargonException { + if(username == null || username.isEmpty() || password == null || password.isEmpty()) { + throw new DataGridAuthenticationException("Username or password invalid: null or empty value(s) provided"); + } else if (username.equalsIgnoreCase(IRODS_ANONYMOUS_ACCOUNT)) { + throw new DataGridAuthenticationException("Cannot log in as anonymous"); + } + + AuthResponse authResponse; + + // Getting iRODS protocol set + logger.debug("Creating IRODSAccount object."); + this.irodsAccount = IRODSAccount.instance(this.irodsHost, Integer.parseInt(this.irodsPort), username, password, + "", this.irodsZoneName, "demoResc"); + this.irodsAccount.setAuthenticationScheme(AuthScheme.findTypeByString(this.irodsAuthScheme)); + logger.debug("Done."); + + logger.debug( + "Authenticating IRODSAccount:\n\tusername: {}\n\tpassword: ***********\n\tirodsHost: {}\n\tirodsZone: {}", + username, this.irodsHost, this.irodsZoneName); + authResponse = this.irodsAccessObjectFactory.authenticateIRODSAccount(this.irodsAccount); + logger.debug("Done."); + + if (authResponse.isSuccessful()) { + + if (StringUtils.isEmpty(authResponse.getAuthMessage())) { + logger.debug("AuthMessage: {}", authResponse.getAuthMessage()); + } + + // Settings iRODS account + this.irodsAccount = authResponse.getAuthenticatingIRODSAccount(); + + // Retrieving logging user + UserAO userAO = this.irodsAccessObjectFactory.getUserAO(this.irodsAccount); + User irodsUser = userAO.findByName(username); + + // If the user is found and has administrator permissions + if (irodsUser.getUserType().equals(UserTypeEnum.RODS_ADMIN) + || irodsUser.getUserType().equals(UserTypeEnum.RODS_USER)) { + + // If the user is not yet persisted in our database + DataGridUser user = this.userDao.findByUsernameAndZone(irodsUser.getName(), irodsUser.getZone()); + + if (user == null) { + user = new DataGridUser(); + user.setUsername(irodsUser.getName()); + user.setAdditionalInfo(irodsUser.getZone()); + user.setDataGridId(Long.parseLong(irodsUser.getId())); + user.setEnabled(true); + user.setFirstName(""); + user.setLastName(""); + if (irodsUser.getUserType().equals(UserTypeEnum.RODS_ADMIN)) { + user.setUserType(UserTypeEnum.RODS_ADMIN.getTextValue()); + } else { + user.setUserType(UserTypeEnum.RODS_USER.getTextValue()); + } + this.userDao.save(user); + } + + this.user = user; + } + } + + return authResponse; + } + + /** + * @return the irodsHost + */ + public String getIrodsHost() { + return this.irodsHost; + } + + /** + * @param irodsHost + * the irodsHost to set + */ + public void setIrodsHost(String irodsHost) { + this.irodsHost = irodsHost; + } + + /** + * @return the irodsPort + */ + public String getIrodsPort() { + return this.irodsPort; + } + + /** + * @param irodsPort + * the irodsPort to set + */ + public void setIrodsPort(String irodsPort) { + this.irodsPort = irodsPort; + } + + /** + * @return the irodsZoneName + */ + public String getIrodsZoneName() { + return this.irodsZoneName; + } + + /** + * @param irodsZoneName + * the irodsZoneName to set + */ + public void setIrodsZoneName(String irodsZoneName) { + this.irodsZoneName = irodsZoneName; + } + + /** + * Temporary implementation of the GrantedAuthority interface for Admin authentication + */ + private class IRODSAdminGrantedAuthority implements GrantedAuthority { + + private static final long serialVersionUID = 357603546013216540L; + + @Override + public String getAuthority() { + return "ROLE_ADMIN"; + } + } + + /** + * Temporary implementation of the GrantedAuthority interface for User authentication + */ + private class IRODSUserGrantedAuthority implements GrantedAuthority { + + private static final long serialVersionUID = 357603546013216540L; + + @Override + public String getAuthority() { + return "ROLE_USER"; + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/auth/IRODSLogoutSuccessHandler.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/auth/IRODSLogoutSuccessHandler.java new file mode 100755 index 000000000..d132a7280 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/auth/IRODSLogoutSuccessHandler.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.auth; + +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.connection.IRODSAccount; +import org.irods.jargon.core.pub.IRODSAccessObjectFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; + +public class IRODSLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler { + + @Autowired + IRODSAccessObjectFactory irodsAccessObjectFactory; + + private static final Logger logger = LoggerFactory.getLogger(IRODSLogoutSuccessHandler.class); + + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { + + logger.info("Logging out..."); + + try { + IRODSAccount irodsAccount = ((UserTokenDetails) authentication.getDetails()).getIrodsAccount(); + String username = irodsAccount.getUserName(); + + logger.info("Closing session and eating all exceptions"); + irodsAccessObjectFactory.closeSessionAndEatExceptions(irodsAccount); + irodsAccessObjectFactory.closeSessionAndEatExceptions(); + + logger.debug("Removing current session temporary directory for file upload"); + try { + File tmpSessionDir = new File(username); + if (tmpSessionDir.exists()) { + FileUtils.forceDelete(tmpSessionDir); + } + } + catch (Exception e) { + logger.error("User {} temporary directory for upload does not exist.", username); + } + + response.sendRedirect("/emc-metalnx-web/login/"); + logger.info("User {} disconnected successfully", username); + } + catch (Exception e) { + logger.info("User session is already expired. There is no need to clear session."); + } + + super.onLogoutSuccess(request, response, authentication); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/auth/UserTokenDetails.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/auth/UserTokenDetails.java new file mode 100755 index 000000000..8a7a004ef --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/auth/UserTokenDetails.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.auth; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import org.irods.jargon.core.connection.IRODSAccount; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.IRODSAccessObjectFactory; +import org.irods.jargon.core.pub.IRODSFileSystem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The object that is encapsulated in the user session + * + */ +public class UserTokenDetails { + + private DataGridUser user; + private IRODSAccount irodsAccount; + + private static final Logger logger = LoggerFactory.getLogger(UserTokenDetails.class); + + /** + * @return the irodsAccount + */ + public IRODSAccount getIrodsAccount() { + return irodsAccount; + } + /** + * @param irodsAccount the irodsAccount to set + */ + public void setIrodsAccount(IRODSAccount irodsAccount) { + this.irodsAccount = irodsAccount; + } + /** + * @return the irodsFileSystem + */ + public IRODSFileSystem getIrodsFileSystem() { + try { + return IRODSFileSystem.instance(); + } catch (JargonException e) { + logger.error("Could not get instance of IRODSFileSystem: ", e); + } + return null; + } + + /** + * @return the irodsAccessObjectFactory + */ + public IRODSAccessObjectFactory getIrodsAccessObjectFactory() { + try { + return this.getIrodsFileSystem().getIRODSAccessObjectFactory(); + } catch (JargonException e) { + logger.error("Could not get Access Object Factory from IRODS: ", e); + } + return null; + } + + /** + * @return the user + */ + public DataGridUser getUser() { + return user; + } + /** + * @param user the user to set + */ + public void setUser(DataGridUser user) { + this.user = user; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/configuration/ConfigServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/configuration/ConfigServiceImpl.java new file mode 100755 index 000000000..1a4897601 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/configuration/ConfigServiceImpl.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.configuration; + +import com.emc.metalnx.services.interfaces.ConfigService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Class that will load all all configurable parameters from *.properties files. + */ +@Service +@Transactional +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES) +public class ConfigServiceImpl implements ConfigService { + + @Value("${msi.api.version}") + private String msiAPIVersionSupported; + + @Value("${msi.metalnx.list}") + private String mlxMSIsExpected; + + @Value("${msi.irods.list}") + private String irods41MSIsExpected; + + @Value("${msi.irods.42.list}") + private String irods42MSIsExpected; + + @Value("${msi.other.list}") + private String otherMSIsExpected; + + @Value("${irods.host}") + private String irodsHost; + + @Value("${irods.port}") + private String irodsPort; + + @Value("${irods.zoneName}") + private String irodsZone; + + @Value("${jobs.irods.username}") + private String irodsJobUser; + + @Value("${jobs.irods.password}") + private String irodsJobPassword; + + @Value("${jobs.irods.auth.scheme}") + private String irodsAuthScheme; + + @Value("${populate.msi.enabled}") + private boolean populateMsiEnabled; + + public String getMsiAPIVersionSupported() { + if (msiAPIVersionSupported == null) return ""; + return msiAPIVersionSupported; + } + + public List getMlxMSIsExpected() { + if (mlxMSIsExpected == null) return Collections.emptyList(); + return Arrays.asList(mlxMSIsExpected.split(",")); + } + + public List getIrods41MSIsExpected() { + if (irods41MSIsExpected == null) return Collections.emptyList(); + return Arrays.asList(irods41MSIsExpected.split(",")); + } + + public List getIrods42MSIsExpected() { + if (irods42MSIsExpected == null) return Collections.emptyList(); + return Arrays.asList(irods42MSIsExpected.split(",")); + } + + public List getOtherMSIsExpected() { + if (otherMSIsExpected == null) return Collections.emptyList(); + return Arrays.asList(otherMSIsExpected.split(",")); + } + + public String getIrodsHost() { + return irodsHost; + } + + public String getIrodsPort() { + return irodsPort; + } + + public String getIrodsZone() { + return irodsZone; + } + + public String getIrodsJobUser() { + return irodsJobUser; + } + + public String getIrodsJobPassword() { + return irodsJobPassword; + } + + public String getIrodsAuthScheme() { + return irodsAuthScheme; + } + + public boolean isPopulateMsiEnabled() { return populateMsiEnabled; } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/AdminServices.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/AdminServices.java new file mode 100755 index 000000000..dc6397369 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/AdminServices.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import org.irods.jargon.core.pub.CollectionAndDataObjectListAndSearchAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.SpecificQueryAO; +import org.irods.jargon.core.pub.UserAO; + +/** + * Service that allows the user to get an instance of each iRODS AO by session. + * + */ +public interface AdminServices { + + /** + * Gets the UserAO from iRODS based on the logged user. + * + * @return the UserAO object + * @throws DataGridConnectionRefusedException + */ + public UserAO getUserAO() throws DataGridConnectionRefusedException; + + /** + * Gets SpecificQueryAO from iRODS based on the admin user set only for Metalnx purposes. + * + * @return instance of SpecificQueryAO + * @throws DataGridConnectionRefusedException + */ + public SpecificQueryAO getSpecificQueryAO() throws DataGridConnectionRefusedException; + + /** + * Gets the DataObjectAO from iRODS based on the admin user set only for Metalnx purposes. + * + * @return Data Object access object + * @throws DataGridConnectionRefusedException + */ + public DataObjectAO getDataObjectAO() throws DataGridConnectionRefusedException; + + /** + * Gets CollectionAndDataObjectListAndSearchAO from iRODS based on an admin user set + * only for Metalnx purposes. + * + * @throws DataGridConnectionRefusedException + */ + public CollectionAndDataObjectListAndSearchAO getCollectionAndDataObjectListAndSearchAO() throws DataGridConnectionRefusedException; + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/AuthenticationProviderService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/AuthenticationProviderService.java new file mode 100755 index 000000000..ef6d94f81 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/AuthenticationProviderService.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import org.springframework.security.authentication.AuthenticationProvider; + +public interface AuthenticationProviderService extends AuthenticationProvider { + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/CollectionService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/CollectionService.java new file mode 100755 index 000000000..c98803cd5 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/CollectionService.java @@ -0,0 +1,421 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.irods.jargon.core.exception.FileNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.query.CollectionAndDataObjectListingEntry; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridDataNotFoundException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridQueryException; + +public interface CollectionService { + + /** + * Verifies whether or not a file already exists in a collection + * + * @param filename + * name of the file to be checked + * @param collectionPath + * path to the collection where the file may or may not exist + * @return True, if a file with the exact same name is found in the collection. + * False, otherwise. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid. + * @throws JargonException + */ + boolean isFileInCollection(String filename, String collectionPath) + throws DataGridConnectionRefusedException, JargonException; + + /** + * Checks whether a path is valid in the grid or not. + * + * @param path + * file or collection path to be validated + * @return True, if the path exists in the grid (path is a file or collection). + * False, otherwise. + */ + boolean isPathValid(String path); + + /** + * Checks whether or not a given path is a path for a collection. + * + * @param path + * @return True, if the given path is a collection path. False, otherwise. + * @throws DataGridException + */ + boolean isCollection(String path) throws DataGridException; + + /** + * Checks whether or not a given path is a path for a data object. + * + * @param path + * @return True, if the given path is a data object path. False, otherwise. + * @throws DataGridException + */ + boolean isDataObject(String path) throws DataGridException; + + /** + * Retrieves all collections and data objects that match a search term. All + * results are paginated. Any collection or data object where the term appears + * in the beginning, middle, and the end of its name will be retrieved. + * + * @param path + * path that we will be looking for collections and data objects that + * match the search term + * @param searchText + * term to be matched + * @param pageNum + * the page the user is currently seeing + * @param pageSize + * number of items shown by page + * @param pageContext + * object that contains information about the number of items + * diplayed and the total number of items found + * @return List of collections and data objects where the term appears. + * @throws DataGridDataNotFoundException + * @throws DataGridQueryException + * @throws DataGridException + */ + List getSubCollectionsAndDataObjectsUnderPathThatMatchSearchTextPaginated( + String path, String searchText, int pageNum, int pageSize, int orderColumn, String orderDir, + DataGridPageContext pageContext) + throws DataGridDataNotFoundException, DataGridException, DataGridQueryException; + + /** + * Gets what kind of permission(s) the logged user has on a certain path + * + * @param path + * path to collection or data object in the data grid + * @return permission type (read, write, own) + * @throws DataGridConnectionRefusedException + */ + String getPermissionsForPath(String path) throws DataGridConnectionRefusedException; + + /** + * Gets the total number of replicas for a specific data object + * + * @param path + * path to the data object + * @return the total number of replicas for the data object given as a parameter + * @throws DataGridConnectionRefusedException + * @throws DataGridException + */ + int getTotalNumberOfReplsForDataObject(String path) throws DataGridException; + + /** + * Lists all replicas of a data object by resource. + * + * @param path + * path to the data object + * @return Map, containing the replica of the given path + * and the resource where this replica is located + * @throws DataGridConnectionRefusedException + */ + Map listReplicasByResource(String path) + throws DataGridConnectionRefusedException; + + /** + * Gets the current user's home directory + * + * @return string with the path to the the current user's home directory + * @throws DataGridException + */ + String getHomeDirectyForCurrentUser() throws DataGridException; + + /** + * Gets all collections and data objects existing under a specific path + * + * @param path + * path to the collection where we will retrieve sub collections and + * data objects + * @return list of collections and data objects existing under a path + * @throws DataGridConnectionRefusedException + * @throws FileNotFoundException + * @throws JargonException + */ + List getSubCollectionsAndDataObjectsUnderPath(String path) + throws DataGridConnectionRefusedException, FileNotFoundException, JargonException; + + /** + * Create a collection in iRODS + * + * @param newCollection + * @throws DataGridException + */ + boolean createCollection(DataGridCollectionAndDataObject newCollection) throws DataGridException; + + /** + * Find collections that match the parameter name + * + * @param name + * name that will be looked for in the grid + * @return CollectionAndDataObjectListingEntry List list of collections that + * match a given name + * @throws DataGridConnectionRefusedException + */ + List searchCollectionAndDataObjectsByName(String name) + throws DataGridConnectionRefusedException; + + /** + * Find collections and data objects that match the parameter name + * + * @param path + * path that will be looked for in the grid + * @return DataGridCollectionAndDataObject of the given path null if no + * collections or data objects were found + * @throws DataGridException + */ + DataGridCollectionAndDataObject findByName(String path) throws DataGridException; + + /** + * Changes collection's name + * + * @param previousPath + * collection path that will be modified + * @param newPath + * new collection path where the previous collection path will be + * moved to + * @param inheritOption + * boolean that says if we need to enable inheritance on a collection + * or not + * @throws DataGridConnectionRefusedException + */ + boolean modifyCollectionAndDataObject(String previousPath, String newPath, boolean inheritOption) + throws DataGridConnectionRefusedException; + + /** + * Lists all the collection that have read permissions on the specified path for + * the given user. + * + * @param path + * @param userName + * @return + * @throws DataGridConnectionRefusedException + */ + Set listReadPermissionsForPathAndUser(String path, String userName) + throws DataGridConnectionRefusedException; + + /** + * Lists all the collection that have write permissions on the specified path + * for the given user. + * + * @param path + * @param userName + * @return + * @throws DataGridConnectionRefusedException + */ + Set listWritePermissionsForPathAndUser(String path, String userName) + throws DataGridConnectionRefusedException; + + /** + * Lists all the collection that have ownership on the specified path for the + * given user. + * + * @param path + * @param userName + * @return + * @throws DataGridConnectionRefusedException + */ + Set listOwnershipForPathAndUser(String path, String userName) throws DataGridConnectionRefusedException; + + /** + * Lists all the collection that have read permissions on the specified path for + * the given group. + * + * @param path + * @param groupName + * @return + * @throws DataGridConnectionRefusedException + */ + Set listReadPermissionsForPathAndGroup(String path, String groupName) + throws DataGridConnectionRefusedException; + + /** + * Lists all the collection that have write permissions on the specified path + * for the given group. + * + * @param path + * @param groupName + * @return + * @throws DataGridConnectionRefusedException + */ + Set listWritePermissionsForPathAndGroup(String path, String groupName) + throws DataGridConnectionRefusedException; + + /** + * Lists all the collection that have ownership on the specified path for the + * given group. + * + * @param path + * @param groupName + * @return + * @throws DataGridConnectionRefusedException + */ + Set listOwnershipForPathAndGroup(String path, String groupName) throws DataGridConnectionRefusedException; + + /** + * List the collections that have inheritance enabled + * + * @param path + * the parent path + * @return list of collections that have the inheritance option enabled + * @throws DataGridConnectionRefusedException + */ + Set listInheritanceForPath(String path) throws DataGridConnectionRefusedException; + + /** + * Update the inheritance options on collections + * + * @param toAdd + * @param toRemove + * @return confirmation + * @throws DataGridConnectionRefusedException + */ + boolean updateInheritanceOptions(Map toAdd, Map toRemove, String zoneName) + throws DataGridConnectionRefusedException; + + /** + * Calculates all files existing in the data grid + * + * @return the number of existing collections + * @throws DataGridConnectionRefusedException + */ + int countAll() throws DataGridConnectionRefusedException; + + /** + * Lists all the collection that have write permissions on the specified path + * recursively for the given group. + * + * @param path + * @param groupName + * @return + * @throws DataGridConnectionRefusedException + * @throws JargonException + */ + Set listWritePermissionsForPathAndGroupRecursive(String path, String groupName) + throws DataGridConnectionRefusedException, JargonException; + + /** + * Prepares files to be downloaded by compressing them into a single file. + * + * @param paths + * array of strings that represent all paths that will be downloaded + * @return Path to the compressed file, if any. Empty string, otherwise. + * @throws IOException + * @throws DataGridException + */ + String prepareFilesForDownload(String[] paths) throws IOException, DataGridException; + + /** + * Prepares files to be downloaded by compressing them into a single file. + * + * @param sourcePaths + * list of files to compress into a single file + * @return Path to the compressed file, if any. Empty string, otherwise. + * @throws IOException + * @throws DataGridException + */ + String prepareFilesForDownload(List sourcePaths) throws IOException, DataGridException; + + /** + * Returns the inheritance option value for a given collection + * + * @param collPath + * @return the boolean + * @throws DataGridConnectionRefusedException + */ + boolean getInheritanceOptionForCollection(String collPath) throws DataGridConnectionRefusedException; + + /** + * Gets the replica number of a collection or data object in the grid. + * + * @param path + * path to the collection/object + * @return int with the replica number 0, if path does not exist + * @throws DataGridConnectionRefusedException + */ + int getReplicationNumber(String path) throws DataGridConnectionRefusedException; + + /** + * Gets the data grid checksum for a given path. + * + * @param path + * path to the collection/object in the grid + * @return String with the checksum + * @throws DataGridConnectionRefusedException + */ + String getChecksum(String path) throws DataGridConnectionRefusedException; + + /** + * Maps a CollectionAndDataObjectListingEntry list into a + * DataGridCollectionAndDataObject list + * + * @param entries + * CollectionAndDataObjectListingEntry objects to map + * @return list of DataGridCollectionAndDataObject objects + */ + List mapListingEntryToDataGridCollectionAndDataObject( + List entries); + + /** + * Maps a CollectionAndDataObjectListingEntry object into a + * DataGridCollectionAndDataObject object + * + * @param entry + * CollectionAndDataObjectListingEntry objects to map + * @return instance of DataGridCollectionAndDataObject + */ + DataGridCollectionAndDataObject mapListingEntryToDataGridCollectionAndDataObject( + CollectionAndDataObjectListingEntry entry); + + /** + * Gets the public directory + * + * @return string with the path to the the public directory + */ + String getHomeDirectyForPublic(); + + /** + * Retrieve only the collections under a parent collection + * + * @param parent + * @return + * @throws DataGridConnectionRefusedException + */ + List getSubCollectionsUnderPath(String parent) + throws DataGridConnectionRefusedException; + + /** + * Get trash path related to the current path + * + * @param path + * @return correspondent trash for given path + */ + String getTrashForPath(String path); + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ConfigService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ConfigService.java new file mode 100755 index 000000000..418c464b3 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ConfigService.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import java.util.List; + +/** + * Service used to retrieve all configurable parameters from *.properties files. + */ +public interface ConfigService { + /** + * Finds the MSI API version supported by the current version of Metalnx. + * @return string representing the version + */ + String getMsiAPIVersionSupported(); + + /** + * Finds the list of all expected Metalnx microservices. + * @return list of all Metalnx microservices. + */ + List getMlxMSIsExpected(); + + /** + * Finds the list of all expected iRODS 4.1.X microservices. + * @return list of all iRODS 4.1.X microservices. + */ + List getIrods41MSIsExpected(); + + /** + * Finds the list of all expected irods 4.2.X microservices. + * @return list of all irods 4.2.X microservices. + */ + List getIrods42MSIsExpected(); + + /** + * Finds the list of all third-party microservices. + * @return list of all third-party microservices. + */ + List getOtherMSIsExpected(); + + /** + * Find the iCAT hostname. + * @return String representing the iCAT machine's hostname. + */ + String getIrodsHost(); + + /** + * Find the irods port number. + * @return String representing irods port number. + */ + String getIrodsPort(); + + /** + * Find the irods default zone. + * @return String representing the irods default zone. + */ + String getIrodsZone(); + + /** + * Find the jobs username. + * @return String representing the username used for synchronizing Metalnx and iRODS. + */ + String getIrodsJobUser(); + + /** + * Find the jobs password. + * @return String representing the password used for synchronizing Metalnx and iRODS. + */ + String getIrodsJobPassword(); + + /** + * Find the authentication scheme used for authenticating against iRODS. + * @return String representing the authentication scheme. + */ + String getIrodsAuthScheme(); + + /** + * Checks whether or not the populate MSI flag is enabled + * @return True, if populate is enabled. False, otherwise. + */ + boolean isPopulateMsiEnabled(); +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/FavoritesService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/FavoritesService.java new file mode 100755 index 000000000..8e7178e3b --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/FavoritesService.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserFavorite; + +import java.util.List; +import java.util.Set; + +public interface FavoritesService { + + /** + * Updates the favorites table for a user, whether be it to remove or add a path + * + * @param user + * @param set + * of paths to be added + * @param set + * of paths to be removed + * @return True, if operation is successful. False, otherwise. + */ + boolean updateFavorites(DataGridUser user, Set toAdd, Set toRemove); + + /** + * Returns a list of strings with each of them representing a path marked as favorite + * + * @param user + * @return List of paths marked as favorites by the user. + */ + List findFavoritesForUserAsString(DataGridUser user); + + /** + * Removes path from database. This operation is used when the corresponding collection or file + * is deleted from the grid + * + * @param path + * @return True, if operation is successful. False, otherwise. + */ + boolean removeFavoriteBasedOnPath(String path); + + /** + * Removes path from database. This operation is used when the corresponding collection or file + * is deleted from the grid + * + * @param {@link DataGridUser} user + * @return True, if operation is successful. False, otherwise. + */ + boolean removeFavoriteBasedOnUser(DataGridUser user); + + /** + * Removes path from database. This operation is used when the corresponding collection or file + * is deleted from the grid + * + * @param path + * @return True, if operation is successful. False, otherwise. + */ + boolean removeFavoriteBasedOnRelativePath(String path); + + /** + * Checks whether the parameter path is a favorite for parameter user + * + * @param user + * @param path + * @return True, if path is a favorite for user. False, otherwise. + */ + boolean isPathFavoriteForUser(DataGridUser user, String path); + + /** + * Find list of favorites paginated + * + * @param user + * @param offset + * represents the starting row in the query + * @param limit + * how many elements will have the result + * @param searchString + * is different from null if filter is used + * @param orderBy + * order by a column + * @param orderDir + * the direction of the order can be 'desc' or 'asc' + * @param onlyCollections + * indicates if the results should contain only collections + * @return list of {@link DataGridUserFavorite} + */ + List findFavoritesPaginated(DataGridUser user, int offset, int limit, String searchString, String orderBy, String orderDir, + boolean onlyCollections); + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/FileOperationService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/FileOperationService.java new file mode 100755 index 000000000..8536b6fae --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/FileOperationService.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridChecksumException; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridReplicateException; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; + +public interface FileOperationService { + + /** + * Copy a file or collection between two locations in the data grid. + * + * @param sourcePath origin path + * @param dstPath destination path + * @param copyWithMetadata flag that says whether or not we are copying the file along with its metadata tags + * @return True, if file or collection was moved. False, otherwise. + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the data grid + */ + boolean copy(String sourcePath, String dstPath, boolean copyWithMetadata) throws DataGridConnectionRefusedException; + + /** + * Copy a set of files or collections between two locations in the data grid. + * + * @param sourcePaths list of paths to be copied + * @param dstPath path where the files/collections will be copied + * @param copyWithMetadata flag that says whether or not we are copying the file along with its metadata tags + * @return True, if all files or collections were moved. False, otherwise. + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the data grid + */ + boolean copy(List sourcePaths, String dstPath, boolean copyWithMetadata) throws DataGridConnectionRefusedException; + + /** + * Delete a file or collection in iRODS + * + * @param path path to the collection or data object to be deleted + * @param force delete collection/data object with force flag set + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the data grid + */ + boolean deleteItem(String path, boolean force) throws DataGridConnectionRefusedException; + + /** + * Delete a collection in iRODS + * + * @param collectionPath path to the collection to be deleted + * @param forceFlag when set to true, force delete an object (-f). When set to false, delete object no with force + * @throws DataGridException if an error occurred during deletion + */ + boolean deleteCollection(String collectionPath, boolean forceFlag) throws DataGridException; + + /** + * Delete a data object in iRODS + * + * @param dataObjectPath path to the data object that will de removed + * @param forceFlag when set to true, force delete an object (-f). When set to false, delete object no with force + * @throws DataGridException if an error occurred during deletion + */ + boolean deleteDataObject(String dataObjectPath, boolean forceFlag) throws DataGridException; + + /** + * Delete a replica of a data object + * + * @param path path to the parent of the data object to be deleted + * @param replicaNumber number of the replica that is going to be deleted + * @param inAdminMode run the command as admin (-M option) + * @return true if the operation was successful and false otherwise + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the data grid + */ + boolean deleteReplica(String path, String fileName, int replicaNumber, boolean inAdminMode) throws DataGridConnectionRefusedException; + + /** + * Download a file or collection from the data grid. + * + * @param path file to download + * @param httpResponse response to an http request + * @param removeTempCollection flag when set to true tells Metalnx to remove temporary collections and tar files + * created for downloading. When set to false, just puts the file into the HTTP response + * @return True, if file or collection was downloaded. False, otherwise. + * @throws DataGridException if an error happen in the data grid + * @throws IOException cannot create the tar ball file + */ + boolean download(String path, HttpServletResponse httpResponse, boolean removeTempCollection) throws DataGridException, IOException; + + /** + * Removes all items existing in the trash folder of a given user. + * + * @param user user who will get the trash cleaned + * @param currentPath path from which the trash path will be extracted + * @return True, if all trash items were removed. False, otherwise. + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the data grid + */ + boolean emptyTrash(DataGridUser user, String currentPath) throws DataGridConnectionRefusedException; + + /** + * Move a file or collection between two locations in the data grid. + * + * @param sourcePath origin path + * @param targetPath destination path where the file will be moved to + * @return True, if file or collection was moved. False, otherwise. + * @throws DataGridException if an error occurred during the move operation + */ + boolean move(String sourcePath, String targetPath) throws DataGridException; + + /** + * Replicates a file into another resource. + * + * @param path path to the file to be replicated + * @param targetResource resource where the replica will be stored + * @param inAdminMode replicate object in admin mode (-M option) + * @throws DataGridReplicateException is thrown if replication fails + * @throws DataGridConnectionRefusedException is thrown if Metalnx cannot connect to the data grid + */ + void replicateDataObject(String path, String targetResource, boolean inAdminMode) throws DataGridConnectionRefusedException, DataGridReplicateException; + + /** + * Computes checksum for a given path. + * @param path path to the file in the grid + * @param filename name of the file to compute checksum + * @throws DataGridChecksumException is thrown if checksum cannot be calculated + * @throws DataGridConnectionRefusedException is thrown when Metalnx cannot connect to the data grid + */ + void computeChecksum(String path, String filename) throws DataGridChecksumException, DataGridConnectionRefusedException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/FilePropertyService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/FilePropertyService.java new file mode 100755 index 000000000..4875118ca --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/FilePropertyService.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import java.util.List; + +import org.irods.jargon.core.exception.JargonException; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridFilePropertySearch; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; + +public interface FilePropertyService { + + /** + * Get all collections and data objects that match any metadata search criteria + * given as a parameters + * + * @param searchList + * list of metadata search criteria + * @param pageContext + * pagination context for proper counting display at the front end + * @param pageNum + * page required + * @param pageSize + * max number of items to display in a page + * @return list of collections and data objects + * @throws DataGridConnectionRefusedException + * @throws JargonException + */ + public List findByFileProperties(List searchList, + DataGridPageContext pageContext, int pageNum, int pageSize) + throws DataGridConnectionRefusedException, JargonException; + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/GroupBookmarkService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/GroupBookmarkService.java new file mode 100755 index 000000000..7b0ff859a --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/GroupBookmarkService.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridGroupBookmark; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import org.irods.jargon.core.exception.DataNotFoundException; +import org.irods.jargon.core.exception.JargonException; + +import java.util.List; +import java.util.Set; + +public interface GroupBookmarkService { + + /** + * Finds all groups a user belongs to and all bookmarks set for this group to have access to. + * + * @param user + * name of the user + * @param additionalInfo + * zone name where the user is + * @return true if the group was created successfully, false otherwise + * @throws DataGridConnectionRefusedException + * @throws JargonException + * @throws DataNotFoundException + */ + public List getGroupsBookmarks(String user, String additionalInfo) throws DataGridConnectionRefusedException, + DataNotFoundException, JargonException; + + /** + * Updates the list of bookmarks on a group + * + * @param group + * @param toAdd + * @param toRemove + * @return a confirmation that the operation has been successful + */ + public boolean updateBookmarks(DataGridGroup group, Set toAdd, Set toRemove); + + /** + * Lists all the bookmarks on a given path + * + * @param path + * @return + */ + public List findBookmarksOnPath(String path); + + /** + * Lists all the bookmarks on a given path for a given group as Strings + * + * @param group + * @param parentPath + * @return + */ + public List findBookmarksForGroupAsString(DataGridGroup group); + + /** + * Remove any bookmark associated with the given path. + * + * @param path + * @return True, if there was one or more bookmarks mathching the given path and they were + * removed successfully. False, + * otherwise. + */ + public boolean removeBookmarkBasedOnPath(String path); + + /** + * Remove any bookmark associated with the given user. + * + * @param {@link DataGridGroup} group + * @return True, if there was one or more bookmarks mathching the given path and they were + * removed successfully. False, + * otherwise. + */ + public boolean removeBookmarkBasedOnGroup(DataGridGroup group); + + /** + * Remove all bookmarks based on relative paths. + * + * @param path + * @return True, if there was one or more bookmarks mathching the given path and they were + * removed successfully. False, + * otherwise. + */ + public boolean removeBookmarkBasedOnRelativePath(String path); + + public List getGroupsBookmarksPaginated(String user, String additionalInfo, int offset, int limit, String searchString, + String orderBy, String orderDir, boolean onlyCollections) throws DataGridConnectionRefusedException, DataNotFoundException, + JargonException; + + public Integer countTotalGroupBookmarks(String user, String additionalInfo) throws DataGridConnectionRefusedException, DataNotFoundException, + JargonException; + + + /** + * Changes an existing bookmark to a new value. + * + * @param oldPath + * existing path that will be updated + * @param newPath + * new path + * @return True, if oldePath was successfully changed to newPath. False, otherwise. + */ + public boolean updateBookmark(String oldPath, String newPath); + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/GroupService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/GroupService.java new file mode 100755 index 000000000..f4de83e3f --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/GroupService.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; + +import java.util.List; +import java.util.Map; + +public interface GroupService { + + /** + * Lists all groups existing on iRODS + * + * @return all groups existing in iRODS + */ + public List findAll(); + + /** + * Find an specific group whose name is equal to groupname + * + * @return all groups existing in iRODS + */ + public List findByGroupname(String groupname); + + /** + * Find an specific group whose name is equal to groupname and zone + * + * @return + */ + public DataGridGroup findByGroupnameAndZone(String groupname, String zone); + + /** + * Creates a group in iRODS + * + * @param newGroup + * @return true if the group was created successfully, false otherwise + * @throws DataGridConnectionRefusedException + */ + public boolean createGroup(DataGridGroup newGroup, List usersToBeAttached) throws DataGridConnectionRefusedException; + + /** + * Removes a group from iRODS + * + * @param groupname + * @return True, if the group was successfully deleted. False, otherwise. + * @throws DataGridConnectionRefusedException + */ + public boolean deleteGroupByGroupname(String groupname) throws DataGridConnectionRefusedException; + + /** + * Attach the user to the group + * + * @param user + * @param group + * @return + * @throws DataGridConnectionRefusedException + */ + public boolean attachUserToGroup(DataGridUser user, DataGridGroup group) throws DataGridConnectionRefusedException; + + /** + * Remove the user from the group + * + * @param user + * @param group + * @return + * @throws DataGridConnectionRefusedException + */ + public boolean removeUserFromGroup(DataGridUser user, DataGridGroup group) throws DataGridConnectionRefusedException; + + /** + * Updates the list of users belonging to a given group + * + * @param user + * @param group + * @return the confirmation + * @throws DataGridConnectionRefusedException + */ + public boolean updateMemberList(DataGridGroup group, List users) throws DataGridConnectionRefusedException; + + /** + * Updates the list of collections the group has read permission + * + * @return the confirmation + * @throws DataGridConnectionRefusedException + */ + public boolean updateReadPermissions(DataGridGroup group, Map addCollectionsToRead, Map removeCollectionsToRead) + throws DataGridConnectionRefusedException; + + /** + * Updates the list of collections the group has write permission + * + * @return the confirmation + * @throws DataGridConnectionRefusedException + */ + public boolean updateWritePermissions(DataGridGroup group, Map addCollectionsToWrite, + Map removeCollectionsToWrite) throws DataGridConnectionRefusedException; + + /** + * Updates the list of collections the group owns + * + * @return the confirmation + * @throws DataGridConnectionRefusedException + */ + public boolean updateOwnership(DataGridGroup group, Map addCollectionsToOwn, Map removeCollectionsToOwn) + throws DataGridConnectionRefusedException; + + /** + * Returns the list of Data Grid IDs for the members of the group. + * + * @param group + * @return list of Data Grid IDs of members + * @throws DataGridConnectionRefusedException + */ + public String[] getMemberList(DataGridGroup group) throws DataGridConnectionRefusedException; + + /** + * Finds users matching the specified query. + * + * @param query + * @param page + * @return list of users + */ + public List findByQueryString(String query); + + /** + * Finds users whose ids match the list of ids. + * + * @param ids + * @return list of users + */ + public List findByDataGridIdList(String[] ids); + + /** + * Finds groups whose names match the list of names. + * + * @param groupNames + * @return list of users + */ + public List findByGroupNameList(String[] groupNames); + + /** + * Finds users whose ids match the list of ids. + * + * @param ids + * @return list of users + */ + public List findByIdList(String[] ids); + + /** + * Calculates the number of existing groups + * + * @return the number of groups + */ + public int countAll(); + + /** + * Gets the group's home collection path in the grid. + * + * Be aware that this method, for performance issues, does not call the data grid + * to verify whether or not the group exists. + * + * @param groupName + * name of the group to find the home path + * @return + * path to the group's home collection + */ + public String getGroupCollectionPath(String groupName); + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/IRODSServices.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/IRODSServices.java new file mode 100755 index 000000000..f18362ccf --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/IRODSServices.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import org.irods.jargon.core.pub.BulkFileOperationsAO; +import org.irods.jargon.core.pub.CollectionAO; +import org.irods.jargon.core.pub.CollectionAndDataObjectListAndSearchAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.DataTransferOperations; +import org.irods.jargon.core.pub.EnvironmentalInfoAO; +import org.irods.jargon.core.pub.IRODSAccessObjectFactory; +import org.irods.jargon.core.pub.IRODSFileSystemAO; +import org.irods.jargon.core.pub.RemoteExecutionOfCommandsAO; +import org.irods.jargon.core.pub.ResourceAO; +import org.irods.jargon.core.pub.RuleProcessingAO; +import org.irods.jargon.core.pub.SpecificQueryAO; +import org.irods.jargon.core.pub.Stream2StreamAO; +import org.irods.jargon.core.pub.UserAO; +import org.irods.jargon.core.pub.UserGroupAO; +import org.irods.jargon.core.pub.ZoneAO; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.irods.jargon.ticket.TicketAdminService; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; + +/** + * Service that allows the user to get an instance of each iRODS AO by session. + * + */ +public interface IRODSServices { + + /** + * Gets an instance of the ticket admin service. + * + * @return TicketAdminService instance + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the grid. + */ + TicketAdminService getTicketAdminService() throws DataGridConnectionRefusedException; + + /** + * Finds what version of iRODS Metalnx is running against. + * + * @return a String representing the version of iRODS. (major.minor.path) + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the grid + */ + String findIRodsVersion() throws DataGridConnectionRefusedException; + + /** + * Gets BulkFileOperationsAO from iRODS. Used for multiple files download. + * + * @return BulkFileOperationsAO instance + * @throws DataGridConnectionRefusedException + */ + BulkFileOperationsAO getBulkFileOperationsAO() throws DataGridConnectionRefusedException; + + /** + * Gets the current user's zone + * + * @return zone name + */ + String getCurrentUserZone(); + + /** + * Gets the logged user + * + * @return logged user name + */ + String getCurrentUser(); + + /** + * Gets the UserAO from iRODS based on the logged user. + * + * @return the UserAO object + * @throws DataGridConnectionRefusedException + */ + UserAO getUserAO() throws DataGridConnectionRefusedException; + + /** + * Gets the GroupAO from iRODS based on the logged user. + * + * @return the UserAO object + * @throws DataGridConnectionRefusedException + */ + UserGroupAO getGroupAO() throws DataGridConnectionRefusedException; + + /** + * Returns the AO of the Collections API + * + * @return CollectionAO object + * @throws DataGridConnectionRefusedException + */ + CollectionAO getCollectionAO() throws DataGridConnectionRefusedException; + + /** + * Returns the AO of the CollectionAndDataObjectListAndSearch API + * + * @return + * @throws DataGridConnectionRefusedException + */ + CollectionAndDataObjectListAndSearchAO getCollectionAndDataObjectListAndSearchAO() + throws DataGridConnectionRefusedException; + + /** + * Gets the IRODSFileSystemAO + * + * @return IRODSFileSystemAO object + * @throws DataGridConnectionRefusedException + */ + IRODSFileSystemAO getIRODSFileSystemAO() throws DataGridConnectionRefusedException; + + /** + * Get access to the iRods File Factory + * + * @return IRODSFileFactory object + * @throws DataGridConnectionRefusedException + */ + IRODSFileFactory getIRODSFileFactory() throws DataGridConnectionRefusedException; + + /** + * This is an access object that can be used to move data to, from, and between + * iRODS resources. + * + * @return DataTransferOperations object + * @throws DataGridConnectionRefusedException + */ + DataTransferOperations getDataTransferOperations() throws DataGridConnectionRefusedException; + + /** + * Get access to the Stream2Stream (useful for file upload) + * + * @return Stream2StreamAO object + * @throws DataGridConnectionRefusedException + */ + Stream2StreamAO getStream2StreamAO() throws DataGridConnectionRefusedException; + + SpecificQueryAO getSpecificQueryAO() throws DataGridConnectionRefusedException; + + RemoteExecutionOfCommandsAO getRemoteExecutionOfCommandsAO() throws DataGridConnectionRefusedException; + + /** + * Gets the ResourceAO from iRODS based on the logged user. + * + * @return Resource access object + * @throws DataGridConnectionRefusedException + */ + ResourceAO getResourceAO() throws DataGridConnectionRefusedException; + + /** + * Gets the ZoneAO from iRODS based on the logged user. + * + * @return Zone access object + * @throws DataGridConnectionRefusedException + */ + ZoneAO getZoneAO() throws DataGridConnectionRefusedException; + + /** + * Gets the DataObjectAO from iRODS based on the logged user. + * + * @return Data Object access object + * @throws DataGridConnectionRefusedException + */ + DataObjectAO getDataObjectAO() throws DataGridConnectionRefusedException; + + /** + * Gets the RuleProcessingAO from iRODS based on the logged user. + * + * @return Rule Processing Access Object + * @throws DataGridConnectionRefusedException + */ + RuleProcessingAO getRuleProcessingAO() throws DataGridConnectionRefusedException; + + /** + * Sets the default storage resource for the current iRODS Account. + * + * @param newResourceName + */ + void setDefaultStorageResource(String newResourceName); + + /** + * Gets the default storage resource for the current iRODS Account. + */ + String getDefaultStorageResource(); + + /** + * Gets the grid environmental info. + * + * @return + * @throws DataGridConnectionRefusedException + */ + EnvironmentalInfoAO getEnvironmentalInfoAO() throws DataGridConnectionRefusedException; + + /** + * Verifies whether or not the version of iRODS is at least 4.2.0. + * + * @return True if iRODS version is >= 4.2.0. False, otherwise. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the grid. + */ + boolean isAtLeastIrods420() throws DataGridConnectionRefusedException; + + /** + * Obtain a reference to the IRODSAccessObjectFactory with hooks to + * manage connections, properties, etc + * + * @return {@link IRODSAccessObjectFactory} reference + */ + IRODSAccessObjectFactory getIrodsAccessObjectFactory(); +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MSIService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MSIService.java new file mode 100755 index 000000000..b684ae89d --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MSIService.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridMSIPkgInfo; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; + +import java.util.List; + +/** + * Service for external software used by Metalnx + */ +public interface MSIService { + + /** + * Retrieves all MSIs packages installed in the grid. + * @param host hostname of the server to get the MSIs installed + * @return {@link DataGridServer} containing the list of MSIs classified by type: metalnx, iRODS, other + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the data grid + */ + DataGridServer getMSIsInstalled(String host) throws DataGridConnectionRefusedException; + + /** + * Retrieves information about the MSI package installed in the servers of the grid. + * @return {@link DataGridMSIPkgInfo} containing information about the MSI package of each server + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the data grid + */ + DataGridMSIPkgInfo getMSIPkgInfo() throws DataGridConnectionRefusedException; + + /** + * Gets the MSI package version installed on all servers. + * @return map + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the grid + */ + List getMSIInfoForAllServers() throws DataGridConnectionRefusedException; + + /** + * Get the MSI package version for a specific server + * @param server server where the MSI package is installed + * @throws DataGridRuleException if cannot execute rule that gets the MSI package version + * @throws DataGridConnectionRefusedException if cannot connect to the grid + */ + void setMSIInfoForServer(DataGridServer server) throws DataGridRuleException, DataGridConnectionRefusedException; + + /** + * Checks whether or not the MSI package version installed, if any, is compatible with this version of the Web App. + * + * @param resource resource name + * @return True, if the MSI package version installed is compatible with this version of the Web App. Otherwise, False. + */ + boolean isMSIAPICompatibleInResc(String resource) throws DataGridConnectionRefusedException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MachineInfoService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MachineInfoService.java new file mode 100755 index 000000000..17a343636 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MachineInfoService.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import java.net.UnknownHostException; + +/** + * This interface defines all information we want to get from a server + * @author guerra + * + */ + +public interface MachineInfoService { + + /** + * Gets the IP Address from a given host name + * @param host + * host name to find the IP address + * @return the IP address in string format + * @throws UnknownHostException + */ + public String getAddress(String host) throws UnknownHostException; + + /** + * Gets the host name from a given ip + * @param ip + * ip to find the host name + * @return the host name relative to the given IP + * @throws UnknownHostException + */ + public String getHostName(String ip) throws UnknownHostException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MetadataService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MetadataService.java new file mode 100755 index 000000000..73db11b38 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MetadataService.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.entity.DataGridMetadataSearch; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; + +import java.util.List; + + +public interface MetadataService { + + /** + * Get all collections and data objects that match any metadata search criteria given as a + * parameters + * + * @param searchList list of metadata search criteria + * @param pageContext pagination context for proper counting display at the front end + * @param pageNum page required + * @param pageSize max number of items to display in a page + * @return list of collections and data objects + * @throws DataGridConnectionRefusedException + */ + public List findByMetadata(List searchList, + DataGridPageContext pageContext, int pageNum, int pageSize) + throws DataGridConnectionRefusedException; + + /** + * Get all metadata (Attribute/Value/Unit) related to a Collection or DataObject in iRODS + * + * @param path path of the Collection or DataObject from which we will get all metadata related to it + * @return list of metadata objects from a Collection or DataObject + * @throws DataGridConnectionRefusedException + */ + public List findMetadataValuesByPath(String path) throws DataGridConnectionRefusedException; + + /** + * Add metadata (Attribute/Value/Unit) to a Collection or DataObject in iRODS + * + * @param path path of the Collection or DataObject which we will be adding a metadata + * @param attribute name of attribute that is going to be added + * @param value the value of the attribute added + * @param unit if there is a unit to this attribute, it can be inserted here + * @return boolean value meaning success or failure in add operation + * @throws DataGridConnectionRefusedException + */ + public boolean addMetadataToPath(String path, String attribute, String value, String unit) throws DataGridConnectionRefusedException; + + /** + * Add metadata (Attribute/Value/Unit) to a Collection or DataObject in iRODS + * + * @param path path of the Collection or DataObject which we will be adding a metadata + * @param metadata object representing an AVU + * @return True, if metadata was added to path. False, otherwise. + * @throws DataGridConnectionRefusedException + */ + public boolean addMetadataToPath(String path, DataGridMetadata metadata) throws DataGridConnectionRefusedException; + + /** + * Add metadata (Attribute/Value/Unit) to a Collection or DataObject in iRODS + * + * @param path path of the Collection or DataObject which we will be adding a metadata + * @param metadataList list of objects representing AVUs + * @return True, if metadata was added to path. False, otherwise. + * @throws DataGridConnectionRefusedException + */ + public boolean addMetadataToPath(String path, List metadataList) throws DataGridConnectionRefusedException; + + /** + * Modify metadata (Attribute/Value/Unit) by passing the path of the Collection or DataObject, + * the metadata that is going to be updated and the new metadata that will replace the old one as parameters + * + * @param path path of the Collection or DataObject from which we will modify a metadata + * @param oldAttribute name of the attribute that is going to be updated + * @param oldValue the value of the attribute that is going to be modified + * @param oldUnit if there is a unit in the metadata that is going to be altered, it has to be inserted here + * @param newAttribute new name of attribute from the metadata that is going to be modified + * @param newValue the new value of the metadata + * @param newUnit update the unit parameter of the metadata + * @return boolean value meaning success or failure in add operation + * @throws DataGridConnectionRefusedException + */ + public boolean modMetadataFromPath(String path, String oldAttribute, String oldValue, + String oldUnit, String newAttribute, String newValue, String newUnit) + throws DataGridConnectionRefusedException; + + /** + * Removes a metadata (Attribute/Value/Unit) from a path. + * + * @param path path to remove metadata attached to + * @param attribute metadata attribute + * @param value metadata value + * @param unit metadata unit + * @return True, if the metadata (AVU) was removed successfully. False, otherwise. + * @throws DataGridConnectionRefusedException + */ + public boolean delMetadataFromPath(String path, String attribute, String value, String unit) + throws DataGridConnectionRefusedException; + + /** + * Populates a list of objects or collections with current user's permission + * @param objectList + * @throws DataGridConnectionRefusedException + */ + public void populateVisibilityForCurrentUser(List objectList) throws DataGridConnectionRefusedException; + + /** + * Copies metadata existing in a source path to a destination path. + * @param srcPath path to retrieve metadata from + * @param dstPath path to add metadata to + * @return True, if there is metadata and it could be copied or if there is no metadata at all. False, otherwise. + */ + boolean copyMetadata(String srcPath, String dstPath) throws DataGridConnectionRefusedException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MonitoringService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MonitoringService.java new file mode 100755 index 000000000..845fa6c99 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/MonitoringService.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +public interface MonitoringService { + + public String getDataFromHost(String type, String host); + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/PermissionsService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/PermissionsService.java new file mode 100755 index 000000000..471f4cadd --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/PermissionsService.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.*; +import com.emc.metalnx.core.domain.entity.enums.DataGridPermType; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import org.irods.jargon.core.exception.JargonException; + +import java.util.List; + +public interface PermissionsService { + + /** + * Finds the most restrictive permission from a list of paths. + * + * @return string containing the most restrictive permission ("none", "read", "write", or "own") + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the grid + */ + DataGridPermType findMostRestrictivePermission(String... paths) throws DataGridConnectionRefusedException; + + /** + * Retrieves all permissions information about a given path for a given user. + * This path can be a collection or a data object. It also parses the + * results down to groups and users so there are no duplicate results. + * + * @param path + * path to retrieve permissions from + * @param username + * user name to check permissions on the given path + * @return list of {@link DataGridFilePermission} instances + */ + List getPathPermissionDetails(String path, String username) throws JargonException, + DataGridConnectionRefusedException; + + /** + * Retrieves all the permissions information about a given object + * that can be a collection or a data object. It also parses the + * results down to groups and users so there are no duplicate results. + * + * @param path + * @return list of {@link DataGridFilePermission} instances + */ + List getPathPermissionDetails(String path) throws JargonException, + DataGridConnectionRefusedException; + + /** + * Gets all the groups with some kind of permission on the permissions list + * + * @param ufps + * @return list of {@link DataGridGroupPermission} + */ + List getGroupsWithPermissions(List ufps); + + /** + * Gets all the users (not including groups) with some kind of permission on the list + * + * @param ufps + * @return list of {@link DataGridUserPermission} + */ + List getUsersWithPermissions(List ufps); + + /** + * Checks if the logged user can modify a given path + * + * @param path + * @return boolean + * @throws DataGridConnectionRefusedException + */ + boolean canLoggedUserModifyPermissionOnPath(String path) throws DataGridConnectionRefusedException; + + /** + * Updates permission on the given path for the given user or group. + * + * @param permType + * @param userOrGroupName + * user or group name to give permissions + * @param recursive + * flag that says whether or not the given permission should be applied recursively + * @param inAdminMode + * if true, tries to set permission in admin mode (-M option) + * if false, tries to set permission normally (no additional options) + * @param paths to apply permission + * @return a {@link boolean} indicating the status of the request + * @throws DataGridConnectionRefusedException + */ + boolean setPermissionOnPath(DataGridPermType permType, String userOrGroupName, boolean recursive, + boolean inAdminMode, String... paths) throws DataGridConnectionRefusedException; + + /** + * Finds resulting most permissive permission for a given user + * @param obj + * @param user + */ + void resolveMostPermissiveAccessForUser(DataGridCollectionAndDataObject obj, DataGridUser user) throws + DataGridException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/RemoteExecutionService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/RemoteExecutionService.java new file mode 100755 index 000000000..856bb6638 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/RemoteExecutionService.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import org.irods.jargon.core.exception.JargonException; + +import java.io.IOException; + +public interface RemoteExecutionService { + + String execute(String command) throws JargonException, IOException, + DataGridConnectionRefusedException ; + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ResourceService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ResourceService.java new file mode 100755 index 000000000..eb9667341 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ResourceService.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridResourceType; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; + +import java.util.List; + +public interface ResourceService { + + /** + * Deletes all children resources from a specific resource. + * @param dgRescToRemove resource to remove the children from + * @throws DataGridConnectionRefusedException if Metalnx cannot communicate with the grid. + */ + void deleteChildrenFromResource(DataGridResource dgRescToRemove) throws DataGridConnectionRefusedException; + + /** + * Get all resources existing in the data grid + * + * @return List of resources + * @throws DataGridConnectionRefusedException + */ + public List findAll() throws DataGridConnectionRefusedException; + + /** + * Returns all the first-level resources for direct access. + * + * @throws DataGridConnectionRefusedException + */ + public List findFirstLevelResources() throws DataGridConnectionRefusedException; + + /** + * Look for a specific resource existing in the data grid by matching the name parameter + * + * @return The resource found or null if no resource matches the name + * @throws DataGridConnectionRefusedException + */ + public DataGridResource find(String resourceName) throws DataGridConnectionRefusedException; + + /** + * List all resource types available in the data grid + * + * @return list with all resource types + */ + public List listResourceTypes(); + + /** + * Get immediate children of a given resource + * + * @return list with resources names + * @throws DataGridConnectionRefusedException + */ + public List getImmediateChildren(String resourceName) throws DataGridConnectionRefusedException; + + /** + * Create a new resource in the data grid + * + * @throws DataGridConnectionRefusedException + */ + public boolean createResource(DataGridResource newDataGridResource) throws DataGridConnectionRefusedException; + + /** + * Delete a resource from the data grid + * + * @param resourceName + * @return True, if the resource given was deleted. False, otherwise. + */ + public boolean deleteResource(String resourceName); + + /** + * Update resource children + * + * @param resourceName + * @param childrenToBeAdded + * @param childrenToBeRemoved + * @return True, if the resource was updated successfully. False, otherwise. + */ + public boolean updateResource(String resourceName, List childrenToBeAdded, List childrenToBeRemoved); + + /** + * Add child to a given resource + * + * @param child + * @return True, if a child was added to a resource. False, otherwise + */ + public boolean addChildToResource(String resourceName, String child); + + /** + * Get all resource servers existing in the data grid sorted alphabetically + * + * @param resources + * list of all resources existing in the grid + * @return list of all servers + * @throws DataGridConnectionRefusedException + */ + public List getAllResourceServers(List resources) throws DataGridConnectionRefusedException; + + /** + * Get all Isilon servers existing in the data grid sorted alphabetically + * + * @param resources + * list of all resources existing in the grid + * @return list of all Isilon servers + */ + public List getAllIsilonServers(List resources); + + /** + * Get all resources from a particular server. + * + * @param serverName + * name of the server to find resources + * @return List + * list that contains all paths where resources are mounted on + * @throws DataGridConnectionRefusedException + */ + public List getResourcesOfAServer(String serverName) throws DataGridConnectionRefusedException; + + /** + * Get all resources from a particular server. This method requires a resource cache to speed up the process by + * avoiding a call to the grid. + * + * @param serverName + * name of the server to find resources + * @param dataGridResources + * list of all current resources in the grid + * @return List + * list that contains all paths where resources are mounted on + * @throws DataGridConnectionRefusedException + */ + public List getResourcesOfAServer(String serverName, List dataGridResources) + throws DataGridConnectionRefusedException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/RuleDeploymentService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/RuleDeploymentService.java new file mode 100755 index 000000000..e5db2a3e6 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/RuleDeploymentService.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import org.springframework.web.multipart.MultipartFile; + +public interface RuleDeploymentService { + + /** + * Deploys a rule into the grid + * @param file rule file to deploy + */ + void deployRule(MultipartFile file) throws DataGridException; + + /** + * Finds the rule cache directory in the grid + * @return String representing the rule cache directory in the grid + */ + String getRuleCachePath(); + + /** + * Creates the rule cache directory in the grid (//.rulecache) + * @throws DataGridException if the creation of the cache directory fails + */ + void createRuleCache() throws DataGridException; + + /** + * Checks whether or not the rule cache directory already exists in the grid. + * @return True, if //.rulecache path exists. False, otherwise. + */ + boolean ruleCacheExists(); +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/RuleService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/RuleService.java new file mode 100755 index 000000000..30cab1a07 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/RuleService.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import java.util.List; +import java.util.Map; + +import org.irods.jargon.core.exception.FileNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.rule.IRODSRuleExecResultOutputParameter; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; + +public interface RuleService { + + /** + * Executes the remove collection microservice. + * + * @param destResc + * resource where the data object is + * @param objPath + * trash path to be emptied + * @param inAdminMode + * execute empty trash in admin mode or not + * @throws DataGridConnectionRefusedException + * if there is no connection to the grid + * @throws DataGridRuleException + * if an error happens during the rule execution + */ + void execEmptyTrashRule(String destResc, String objPath, boolean inAdminMode) + throws DataGridConnectionRefusedException, DataGridRuleException; + + /** + * Executes the get microservices MSI. + * + * @param host + * server's hostname + * @return List of MSIs on the server that resource is. + */ + List execGetMSIsRule(String host) throws DataGridConnectionRefusedException, DataGridRuleException; + + /** + * Executes the get version MSI. + * + * @param host + * server's hostname + * @return version of the MSI currently installed + * @throws DataGridRuleException + * if an error happens during the rule execution + * @throws DataGridConnectionRefusedException + * if there is no connection to the grid + */ + String execGetVersionRule(String host) throws DataGridRuleException, DataGridConnectionRefusedException; + + /** + * Executes the replicate data object rule + * + * @param destResc + * resource where the data object is + * @param path + * path to the data object + * @param inAdminMode + * True, if the replication has to run as admin. False, otherwise. + * @throws DataGridRuleException + * if replication fails. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid if Metalnx cannot + * connect to the data grid. + */ + void execReplDataObjRule(String destResc, String path, boolean inAdminMode) + throws DataGridRuleException, DataGridConnectionRefusedException; + + /** + * Execute populate metadata rule. + * + * @param host + * hostname of the machine to run the rule + * @param objPath + * path to the object in the data grid path to the object in the data + * grid + * @throws DataGridRuleException + * if rule exection failed. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid + */ + void execPopulateMetadataRule(String host, String objPath) + throws DataGridRuleException, DataGridConnectionRefusedException; + + /** + * Execute metadata extraction from image files rule. + * + * @param host + * hostname of the machine to run the rule + * @param objPath + * path to the object in the data grid + * @param filePath + * physical file path physical file path + * @throws DataGridRuleException + * if rule exection failed. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid if Metalnx cannot + * connect to the data grid + */ + void execImageRule(String host, String objPath, String filePath) + throws DataGridRuleException, DataGridConnectionRefusedException; + + /** + * Execute metadata extraction from VCF files rule. + * + * @param host + * hostname of the machine to run the rule + * @param objPath + * path to the object in the data grid + * @param filePath + * physical file path + * @throws DataGridRuleException + * if rule exection failed. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid + */ + void execVCFMetadataRule(String host, String objPath, String filePath) + throws DataGridRuleException, DataGridConnectionRefusedException; + + /** + * Execute metadata extraction from BAM or CRAM files rule. + * + * @param host + * hostname of the machine to run the rule + * @param objPath + * path to the object in the data grid + * @param filePath + * physical file path + * @throws DataGridRuleException + * if rule exection failed. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid + */ + void execBamCramMetadataRule(String host, String objPath, String filePath) + throws DataGridRuleException, DataGridConnectionRefusedException; + + /** + * Execute metadata extraction from manifest files rule. + * + * @param host + * hostname of the machine to run the rule + * @param targetPath + * target path target path + * @param objPath + * path to the object in the data grid + * @param filePath + * physical file path + * @throws DataGridRuleException + * if rule exection failed. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid + * @throws JargonException + * @throws FileNotFoundException + */ + void execManifestFileRule(String host, String targetPath, String objPath, String filePath) + throws DataGridRuleException, DataGridConnectionRefusedException, FileNotFoundException, JargonException; + + /** + * Execute metadata extraction from Illumina files rule. + * + * @param dgResc + * resource to run the rule + * @param targetPath + * target path + * @param objPath + * path to the object in the data grid + * @throws DataGridRuleException + * if rule exection failed. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid + */ + void execIlluminaMetadataRule(DataGridResource dgResc, String targetPath, String objPath) + throws DataGridRuleException, DataGridConnectionRefusedException; + + /** + * Executes a rule in the data grid + * + * @param rule + * rule string to be executed + * @throws DataGridRuleException + * if rule exection failed. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid + */ + Map executeRule(String rule) + throws DataGridRuleException, DataGridConnectionRefusedException; + + /** + * Executes the deployment rule (for deploying other rules in the grid) + * + * @param host + * machine's hostname where the rule will be run + * @param ruleName + * name of the rule being deployed + * @param ruleVaultPath + * physical rule path into the grid's Vault directory + * @throws DataGridRuleException + * if rule exection failed. + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid + */ + void execDeploymentRule(String host, String ruleName, String ruleVaultPath) + throws DataGridRuleException, DataGridConnectionRefusedException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ServerService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ServerService.java new file mode 100755 index 000000000..422020ba3 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ServerService.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; + +import java.util.HashMap; +import java.util.List; + +public interface ServerService { + + /** + * Gets all (resource) servers from the grid. + * + * @param resources + * list of all resources existing in the grid + * @param serverMapInCache + * host name, DataGridServer object map stored in cache + * @return list of DataGridServer + * @throws DataGridConnectionRefusedException + */ + public List getAllServers(List resources, HashMap serverMapInCache) + throws DataGridConnectionRefusedException; + + /** + * Gets all (resource) servers from the grid. + * + * @param resources + * list of all resources existing in the grid + * @return list of DataGridServer + */ + public List getAllIsilonServers(List resources); + + /** + * Gets all non-resource servers from the grid. + * + * @param servers + * list of servers existing in the grid + * @param serverMapInCache + * host name, DataGridServer object map stored in cache + * @return list of DataGridServer + * @throws DataGridConnectionRefusedException + */ + public List getAllNonResourceServers(List servers, HashMap serverMapInCache) + throws DataGridConnectionRefusedException; + + /** + * Gets all non-resource servers from the grid using a resource cache. This cache is used to avoid + * a call to the grid. + * + * @param servers + * list of servers existing in the grid + * @param serverMapInCache + * host name, DataGridServer object map stored in cache + * @param dataGridResources + * list of all current resources existing in the grid + * @return list of servers + * @throws DataGridConnectionRefusedException + */ + public List getAllNonResourceServers(List servers, HashMap serverMapInCache, + List dataGridResources) throws DataGridConnectionRefusedException; + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/SpecQueryService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/SpecQueryService.java new file mode 100755 index 000000000..e49307189 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/SpecQueryService.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import java.util.List; + +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.query.SpecificQueryResultSet; + +import com.emc.metalnx.core.domain.entity.DataGridFilePropertySearch; +import com.emc.metalnx.core.domain.entity.DataGridMetadataSearch; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.UnsupportedDataGridFeatureException; + +public interface SpecQueryService { + + /** + * Searches data objects or collections by metadata. + * + * @param metadataSearch + * list of metadata criteria to apply the search + * @param zone + * zone to be looking for collections and data objects + * @param searchAgainstColls + * flag set to true when looking for collections and set to false + * when looking for data objects + * @param pageContext + * pagination context + * @param offset + * offset for pagination + * @param limit + * max number of items shown in a page + * @return Query result set from a metadata search + * @throws DataGridConnectionRefusedException + * @throws JargonException + */ + public SpecificQueryResultSet searchByMetadata(List metadataSearch, String zone, + boolean searchAgainstColls, DataGridPageContext pageContext, int offset, int limit) + throws DataGridConnectionRefusedException, JargonException; + + /** + * Searches data objects or collections by file properties. + * + * @param metadataSearch + * list of file properties criteria to apply the search + * @param zone + * zone to be looking for collections and data objects + * @param searchAgainstColls + * flag set to true when looking for collections and set to false + * when looking for data objects + * @param pageContext + * pagination context + * @param offset + * offset for pagination + * @param limit + * max number of items shown in a page + * @return Query result set from a file properties search + * @throws DataGridConnectionRefusedException + * @throws JargonException + */ + public SpecificQueryResultSet searchByFileProperties(List filePropertySearch, + String zone, boolean searchAgainstColls, DataGridPageContext pageContext, int offset, int limit) + throws DataGridConnectionRefusedException, JargonException; + + /** + * Removes a specific query from the data grid by its alias + * + * @param specQueryAlias + * @throws DataGridConnectionRefusedException + */ + public void deleteSpecQueryByAlias(String specQueryAlias) throws DataGridConnectionRefusedException; + + /** + * Counts the total number of Collections matching a specific metadata criteria. + * + * @param metadataSearch + * list of metadata criteria to apply the search + * @param zone + * zone to be looking for collections + * @return total number of collections matching a metadata search criteria + * @throws DataGridConnectionRefusedException + * @throws JargonException + */ + public int countCollectionsMatchingMetadata(List metadataSearch, String zone) + throws DataGridConnectionRefusedException, JargonException; + + /** + * Counts the total number of data objects matching a specific metadata + * criteria. + * + * @param metadataSearch + * list of metadata criteria to apply the search + * @param zone + * zone to be looking for data objects + * @return total number of data objects matching a metadata search criteria + * @throws DataGridConnectionRefusedException + * @throws JargonException + */ + public int countDataObjectsMatchingMetadata(List metadataSearch, String zone) + throws DataGridConnectionRefusedException, JargonException; + + /** + * Counts the total number of Collections matching a specific file properties + * criteria. + * + * @param filePropertiesSearch + * list of file properties criteria to apply the search + * @param zone + * zone to be looking for collections + * @return total number of collections matching a file properties search + * criteria + * @throws DataGridConnectionRefusedException + * @throws JargonException + * @throws UnsupportedDataGridFeatureException + */ + public int countCollectionsMatchingFileProperties(List filePropertiesSearch, + String zone) + throws DataGridConnectionRefusedException, UnsupportedDataGridFeatureException, JargonException; + + /** + * Counts the total number of data objects matching a specific file properties + * criteria. + * + * @param filePropertiesSearch + * list of file properties criteria to apply the search + * @param zone + * zone to be looking for data objects + * @return total number of data objects matching a file properties search + * criteria + * @throws DataGridConnectionRefusedException + * @throws JargonException + * @throws UnsupportedDataGridFeatureException + */ + public int countDataObjectsMatchingFileProperties(List filePropertiesSearch, + String zone) + throws DataGridConnectionRefusedException, UnsupportedDataGridFeatureException, JargonException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/SpecificQueryService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/SpecificQueryService.java new file mode 100755 index 000000000..0ac7e0040 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/SpecificQueryService.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridSpecificQuery; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import org.irods.jargon.core.query.SpecificQueryResultSet; + +import java.util.List; + +public interface SpecificQueryService { + + /** + * Creates a Specific Query object on the underlying datagrid system + * @param specificQuery + * @return + * @throws DataGridConnectionRefusedException + */ + boolean createSpecificQuery(DataGridSpecificQuery specificQuery) throws DataGridConnectionRefusedException; + + /** + * Updates a Specific Query object on the underlying datagrid system + * @param specificQuery + * @return + * @throws DataGridConnectionRefusedException + */ + boolean updateSpecificQuery(DataGridSpecificQuery specificQuery) throws DataGridConnectionRefusedException; + + /** + * Lists all the specific queries on the data grid system + * @return + * @throws DataGridConnectionRefusedException + */ + List findAll() throws DataGridConnectionRefusedException; + + /** + * Lists all the specific queries on the data grid system matching + * the alias name + * @return + * @throws DataGridConnectionRefusedException + */ + DataGridSpecificQuery findByAlias(String alias) throws DataGridConnectionRefusedException; + + /** + * Lists all the specific queries on the data grid system matching + * part of the alias with the 'like' string argument. + * @param like + * @return + * @throws DataGridConnectionRefusedException + */ + List findByAliasLike(String like) throws DataGridConnectionRefusedException; + + /** + * Executes the specific query on the underlying data grid system. + * @param specificQuery + * @return + * @throws DataGridConnectionRefusedException + */ + SpecificQueryResultSet executeSpecificQuery(DataGridSpecificQuery specificQuery, String zone) + throws DataGridConnectionRefusedException; + + /** + * Removes a specific query object from the underlying data grid system. + * @param specificQuery + * @return + * @throws DataGridConnectionRefusedException + */ + boolean removeSpecificQueryByAlias(DataGridSpecificQuery specificQuery) throws DataGridConnectionRefusedException; + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/StorageService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/StorageService.java new file mode 100755 index 000000000..8444ca3da --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/StorageService.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; + +import java.util.List; + +public interface StorageService { + + /** + * Calculates the amount of used storage in the entire grid. + * @return the total of used storage + */ + public long totalUsedStorageOfGrid(List servers); + + /** + * Calculates the amount of available storage in the entire grid. + * @return the total of available storage + */ + public long totalAvailableStorageOfGrid(List servers); + + /** + * Calculates the total used storage of a server. + * @param hostname + * @param partitionsLocation + * partitions existing in the server + * @param diskInfoJson + * JSON response from RMD + * @param currentServerResources + * current resources of a server available in cache (in case the iCAT is down) + * @return totalUsedStorage + * number that represents the amount of storage used + * @throws DataGridConnectionRefusedException + */ + public long totalUsedStorageOfAServer(String hostname, String diskInfoJson, + List currentServerResources) + throws DataGridConnectionRefusedException; + + /** + * Calculates the total available storage a server. + * @param hostname + * server hostname + * @param diskInfoJson + * JSON response from RMD + * @param currentServerResources + * current resources of a server available in cache (in case the iCAT is down) + * @return totalAvailableStorage + * number that represents the amount of storage available + * @throws DataGridConnectionRefusedException + */ + public long totalAvailableStorageOfAServer(String hostname, String diskInfoJson, + List currentServerResources) throws DataGridConnectionRefusedException; + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TemplateFieldService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TemplateFieldService.java new file mode 100755 index 000000000..32b4f23fd --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TemplateFieldService.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateAttrException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateUnitException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateValueException; + +import java.util.List; + +public interface TemplateFieldService { + + /** + * Finds a template field by id + * + * @param id + * id of the template field to be found + * @return DataGridTemplateField object if found. Null, otherwise. + */ + public DataGridTemplateField findById(long id); + + /** + * Creates a template field into the database + * + * @param dataGridTemplateField + * template field to be saved into the database + * @return The id of the template field just created + */ + public long createTemplateField(DataGridTemplateField dataGridTemplateField); + + /** + * Lists all template fields existing in the database. + * + * @return List of template fields + */ + public List findAll(); + + /** + * Deletes a template field from the database + * + * @param dataGridTemplate + * template field object to be removed + * @return True, if the template field was successfully removed. False, otherwise. + */ + public boolean deleteTemplateField(DataGridTemplateField dataGridTemplateField); + + /** + * Modifies a template field from the database based on its id + * + * @param id + * id of the template field to be removed + * @return True, if the template field was successfully modified. False, otherwise. + * @throws DataGridTemplateUnitException + * @throws DataGridTemplateValueException + * @throws DataGridTemplateAttrException + */ + boolean modifyTemplateField(long id, String attribute, String value, String unit) throws DataGridTemplateAttrException, + DataGridTemplateValueException, DataGridTemplateUnitException; + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TemplateService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TemplateService.java new file mode 100755 index 000000000..d56e38118 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TemplateService.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.com.emc.metalnx.core.xml.MlxMetadataTemplate; +import com.emc.metalnx.core.domain.entity.DataGridTemplate; +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateAttrException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateUnitException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateValueException; +import com.emc.metalnx.core.domain.exceptions.DataGridTooLongTemplateNameException; + +import javax.xml.bind.JAXBException; +import java.io.InputStream; +import java.util.List; + +public interface TemplateService { + + /** + * Modifies an existing template + * + * @param template + * template object to persist in the database + * @return True, if the template was successfully modified. False, otherwise. + */ + public boolean modifyTemplate(DataGridTemplate template); + + /** + * Finds a template by id + * + * @param id + * id of the template to be found + * @return DataGridTemplate object if found. Null, otherwise. + */ + public DataGridTemplate findById(long id); + + /** + * Finds a template by a given name + * + * @param templateName + * name of the template to be found + * @return DataGridTemplate object if found. Null, otherwise. + */ + public DataGridTemplate findByName(String templateName); + + /** + * Creates a template into the database + * + * @param dataGridTemplate + * template to be saved into the database + * @return The id of the template just created + */ + public long createTemplate(DataGridTemplate dataGridTemplate); + + /** + * Lists all templates existing in the database. + * + * @return List of templates + */ + public List findAll(); + + /** + * Deletes a template from the database based on its id + * + * @param id + * id of the template to be removed + * @return True, if the template was successfully removed. False, otherwise. + */ + public boolean deleteTemplate(long id); + + /** + * Find templates by a query string + * + * @param queryString + * string containing the search term to match template names + * @return list of templates + */ + public List findByQueryString(String queryString); + + /** + * Lists all fields of a template by its name + * + * @param template + * name of the template + * @return List of template fields, if any. + */ + public List listTemplateFields(String template); + + /** + * Lists all fields of a template by its id + * + * @param id + * name of the template + * @return List of template fields, if any. + */ + public List listTemplateFields(Long id); + + /** + * Lists all the system-wide metadata templates + * + * @return List of template fields, if any. + */ + public List listPublicTemplates(); + + /** + * Returns all the privates templates owned by a given user + * + * @param user + * @return List of template fields, if any. + */ + public List listPrivateTemplatesByUser(String user); + + /** + * Imports a XML file with multiple metadata template definition + * + * @param inStream + * @return + * @throws JAXBException + * @throws DataGridTooLongTemplateNameException + * @throws DataGridTemplateAttrException + * @throws DataGridTemplateValueException + * @throws DataGridTemplateUnitException + */ + public boolean importXmlMetadataTemplate(InputStream inStream, String owner, String prefix, String suffix) throws JAXBException, + DataGridTooLongTemplateNameException, DataGridTemplateAttrException, DataGridTemplateValueException, DataGridTemplateUnitException; + + /** + * Maps a DataGridTemplate to a XML format + * + * @param template + * @return + */ + public MlxMetadataTemplate mapDataGridTemplateToXml(DataGridTemplate template); + + /** + * Returns the number of all templates existing in the database. + * + * @return number of templates + */ + int countAll(); + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TicketClientService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TicketClientService.java new file mode 100755 index 000000000..dc0b62d40 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TicketClientService.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.exceptions.DataGridTicketDownloadException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; + +import java.io.File; + +/** + * Client access to the grid via Tickets. + */ +public interface TicketClientService { + + /** + * Transfers a file to the grid using a ticket. + * @param ticketString ticket string + * @param localFile local file to be transferred to the grid + * @param destPath path where the file will be uploaded to + * @throws DataGridTicketUploadException if ticket string, local file or destination path are not provided + * @throws DataGridTicketInvalidUserException if anonymous account is not valid (account does not exist) + */ + void transferFileToIRODSUsingTicket(String ticketString, File localFile, String destPath) + throws DataGridTicketUploadException, DataGridTicketInvalidUserException; + + /** + * Gets a file from the grid. + * @param ticketString ticket string to access a collection or an object + * @param path path to get files from + * @return {@code File} file + * @throws DataGridTicketInvalidUserException if anonymous account is not valid (account does not exist) + * @throws DataGridTicketDownloadException if any other error happens during download + */ + File getFileFromIRODSUsingTicket(String ticketString, String path) + throws DataGridTicketInvalidUserException, DataGridTicketDownloadException; + + /** + * Deletes the temporary directory created after downloading files from the grid using a ticket. + */ + void deleteTempTicketDir(); +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TicketService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TicketService.java new file mode 100755 index 000000000..cbc8fa165 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/TicketService.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketNotFoundException; + +import java.util.List; + +/** + * Service for tickets in the grid. + */ +public interface TicketService { + + /** + * Finds all tickets existing in the system. + * The tickets found depend on the user who requests the list of tickets. RODS_ADMINs can see all tickets while + * RODS_USERs can only see the tickets they have created. + * @return List of tickets if any found. Empty list is returned if no tickets are found. + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the grid. + */ + List findAll() throws DataGridConnectionRefusedException; + + /** + * Deletes a ticket from the grid by the ticket string + * @param ticketString string that identifies the ticket uniquely + * @return True, if the ticket was deleted successfully. False, otherwise. + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the grid + */ + boolean delete(String ticketString) throws DataGridConnectionRefusedException; + + /** + * Deletes a list of tickets from the grid. + * + * Obs: if the user is a rods admin, all tickets existing in the grid can be deleted. + * @param ticketStrings list of ticket strings that will be deleted + * @return True, if all given tickets were deleted successfully. False, otherwise. + * @throws DataGridConnectionRefusedException if Metalnx cannot connect to the grid. + */ + boolean bulkDelete(List ticketStrings) throws DataGridConnectionRefusedException; + + /** + * Creates a ticket in the grid + * @param dgTicket Ticket to be created. + * @return String representing the ticket string. + * @throws DataGridConnectionRefusedException thrown if Metalnx cannot connect to Metalnx + * @throws DataGridTicketException thrown if an error occurs when setting any ticket parameter + */ + String create(DataGridTicket dgTicket) throws DataGridConnectionRefusedException, DataGridTicketException; + + /** + * Finds a specific ticket by its id or string. + * @param ticketId ticket ID or string + * @return Ticket object if a ticket with the given ID exists. Null is returned otherwise. + * @throws DataGridConnectionRefusedException thrown if Metalnx cannot connect to the grid + * @throws DataGridTicketNotFoundException thrown if ticket cannot be found + */ + DataGridTicket find(String ticketId) throws DataGridConnectionRefusedException, DataGridTicketNotFoundException; + + /** + * Modifies a ticket in the grid + * @param t Ticket to be modified. + * Ticket ID or String has to exist in the grid in order for the ticket to be modified. + * @return DataGridTicket representing the ticket just modified. + * Null is returned if the ticket was not modified. + * @throws DataGridConnectionRefusedException thrown if Metalnx cannot connect to Metalnx + * @throws DataGridTicketException thrown if an error occurs when modifying any ticket parameter + */ + DataGridTicket modify(DataGridTicket t) throws DataGridConnectionRefusedException, DataGridTicketException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UploadService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UploadService.java new file mode 100755 index 000000000..de647583e --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UploadService.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import org.springframework.web.multipart.MultipartFile; + +public interface UploadService { + + /** + * Transfer a file to the data grid without chuncking file. + * + * @param multipartFile + * file to be uploaded + * @param targetPath + * path to which the file is going to be tranferred + * @param computeCheckSum + * True if user checked checksum option in UI + * @param replicateFile + * True if user checked replica option in UI + * @param replicationResource + * resources to which the file is going to be replicated into + * @param destinationResource + * resource in which the file is going to be uploaded + * @param overwriteDuplicateFiles + * option to overwrite in case the file already exists in iRODS + * @return + * @throws DataGridException + */ + boolean upload(MultipartFile multipartFile, String targetPath, boolean computeCheckSum, + boolean replicateFile, String replicationResource, String destinationResource, + boolean overwriteDuplicateFiles) throws DataGridException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UserBookmarkService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UserBookmarkService.java new file mode 100755 index 000000000..a7c73c4b5 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UserBookmarkService.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserBookmark; + +import java.util.List; +import java.util.Set; + +public interface UserBookmarkService { + + /** + * Updates the list of bookmarks on a user + * + * @param user + * @param toAdd + * @param toRemove + * @return a confirmation that the operation has been successful + */ + public boolean updateBookmarks(DataGridUser user, Set toAdd, Set toRemove); + + /** + * Lists all the bookmarks on a given path + * + * @param path + * @return + */ + public List findBookmarksOnPath(String path); + + /** + * Lists all the bookmarks on a given path for a given user as Strings + * + * @param user + * @param parentPath + * @return + */ + public List findBookmarksForUserAsString(DataGridUser user); + + /** + * Remove any bookmark associated with the given path. + * + * @param path + * @return True, if there was one or more bookmarks mathching the given path and they were + * removed successfully. False, + * otherwise. + */ + public boolean removeBookmarkBasedOnPath(String path); + + /** + * Remove any bookmark associated with the given user. + * + * @param {@link DataGridUser} user + * @return True, if there was one or more bookmarks mathching the given user and they were + * removed successfully. False, otherwise. + */ + public boolean removeBookmarkBasedOnUser(DataGridUser user); + + /** + * Remove all bookmarks based on relative paths. + * + * @param path + * @return True, if there was one or more bookmarks mathching the given path and they were + * removed successfully. False, + * otherwise. + */ + public boolean removeBookmarkBasedOnRelativePath(String path); + + /** + * Find list of bookmarks paginated + * + * @param user + * @param start + * represents the starting row in the query + * @param length + * how many elements will have the result + * @param searchString + * is different from null if filter is used + * @param orderBy + * order by a column + * @param orderDir + * the direction of the order can be 'desc' or 'asc' + * @param onlyCollections + * indicates if the results should contain only collections + * @return list of {@link DataGridUserBookmark} + */ + public List findBookmarksPaginated(DataGridUser user, int start, int length, String searchString, String orderBy, + String orderDir, boolean onlyCollections); + + /** + * Find list of bookmarks paginated + * + * @param user + * @param start + * represents the starting row in the query + * @param length + * how many elements will have the result + * @param searchString + * is different from null if filter is used + * @param orderBy + * list of columns the database will order the results by + * @param orderDir + * list of directions to be applied to the columns in the order by clause (DESC or + * ASC) + * @param onlyCollections + * indicates if the results should contain only collections + * @return list of {@link DataGridUserBookmark} + */ + public List findBookmarksPaginated(DataGridUser user, int start, int length, String searchString, List orderBy, + List orderDir, boolean onlyCollections); + + /** + * Updates an existing bookmark path to a new one. + * + * @param oldPath + * old path to be updated + * @param newPath + * new path value + * @return True, if oldPath was successfully set to newPath. False, otherwise. + */ + boolean updateBookmark(String oldPath, String newPath); +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UserProfileService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UserProfileService.java new file mode 100755 index 000000000..838f69c64 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UserProfileService.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.UserProfile; + +import java.util.List; + +public interface UserProfileService { + + /** + * Returns the list of all existing profiles on the local DB. + * + * @return + */ + List findAll(); + + /** + * Finds a UserProfile object by its id. + * + * @param id + * @return + */ + UserProfile findById(Long id); + + /** + * Returns the list of UserProfiles matching the input string. + * + * @param query + * @return + */ + List findByQueryString(String query); + + /** + * Creates a new user profile on the DB. + * + * @param userProfile + */ + Long createUserProfile(UserProfile userProfile); + + /** + * Updates the UserProfile entity on DB. + * + * @param profile + * @return + */ + void modifyUserProfile(UserProfile profile); + + /** + * Removes a user profile from DB. + * + * @param profile + */ + void removeUserProfile(UserProfile profile); + + /** + * Returns the size of the list of all existing profiles on the local DB. + * + * @return + */ + int countAll(); +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UserService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UserService.java new file mode 100755 index 000000000..2e5be9537 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/UserService.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.UserProfile; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import org.irods.jargon.core.exception.JargonException; + +import java.util.List; +import java.util.Map; + +public interface UserService { + + /** + * Returns all the users existing on iRODS + * + * @return + */ + public List findAll(); + + /** + * Returns the list of DataGridUsers matching data grid ID. + * + * @param ids + * @return + */ + List findByDataGridIds(String[] ids); + + /** + * Create the user on local DB and on iRODS + * + * @param user + * @return confirmation + * @throws DataGridConnectionRefusedException + */ + public boolean createUser(DataGridUser user, String password) throws JargonException, DataGridConnectionRefusedException; + + /** + * Delete user by username + * + * @param username + * @return a confirmation that the user has been removed + * @throws DataGridConnectionRefusedException + */ + public boolean deleteUserByUsername(String username) throws DataGridConnectionRefusedException; + + /** + * Modify user by username + * + * @param username + * @return a confirmation that the user has been modified + * @throws DataGridConnectionRefusedException + */ + public boolean modifyUser(DataGridUser modifyUser) throws DataGridConnectionRefusedException; + + /** + * Finds users by username + * + * @param username + * name of the user(s) to be found + * @return List of users matching {@code username} + */ + public List findByUsername(String username); + + /** + * Return a user whose name is 'username' and additional info is 'additionalInfo' + * + * @param username + * @param additionalInfo + * @return + */ + public DataGridUser findByUsernameAndAdditionalInfo(String username, String additionalInfo); + + /** + * Returns the quantity of users persisted on our database. + * + * @return + */ + public int countAll(); + + /** + * Finds users matching the specified query. + * + * @param query + * @param page + * @return list of users + */ + public List findByQueryString(String query); + + /** + * Returns the list of IDs of each group the user belongs to. + * + * @param user + * @return list of data grid IDs + * @throws DataGridConnectionRefusedException + */ + public String[] getGroupIdsForUser(DataGridUser user) throws DataGridConnectionRefusedException; + + /** + * Returns the list of IDs of each group the user belongs to. + * + * @param username + * name of the user + * @param additionalInfo + * zone name where the user is + * @return list of data grid IDs + * @throws DataGridConnectionRefusedException + */ + public String[] getGroupIdsForUser(String username, String additionalInfo) throws DataGridConnectionRefusedException; + + /** + * Updates the list of groups the user belongs to. + * + * @param user + * @param groups + * @return the confirmation of the update. + * @throws DataGridConnectionRefusedException + */ + public boolean updateGroupList(DataGridUser user, List groups) throws DataGridConnectionRefusedException; + + /** + * Applies a profile to a user + * + * @param profile + * @param user + * @return + */ + public boolean applyProfileToUser(UserProfile profile, DataGridUser user); + + /** + * Updates the list of collections the user has read permission + * + * @param group + * @param addCollectionsToRead + * @param removeCollectionsToRead + * @param recursive + * flag that says whether or not the permission is applied recursively + * @return True, if permissions were updated. False, otherwise. + * @throws DataGridConnectionRefusedException + */ + public boolean updateReadPermissions(DataGridUser group, Map addCollectionsToRead, Map removeCollectionsToRead) + throws DataGridConnectionRefusedException; + + /** + * Updates the list of collections the user has write permission + * + * @param group + * @param addCollectionsToWrite + * @param removeCollectionsToWrite + * @param recursive + * flag that says whether or not the permission is applied recursively + * @return True, if permissions were updated. False, otherwise. + * @throws DataGridConnectionRefusedException + */ + public boolean updateWritePermissions(DataGridUser group, Map addCollectionsToWrite, + Map removeCollectionsToWrite) throws DataGridConnectionRefusedException; + + /** + * Updates the list of collections the user owns + * + * @param group + * @param addCollectionsToOwn + * @param removeCollectionsToOwn + * @param recursive + * flag that says whether or not the permission is applied recursively + * @return True, if permissions were updated. False, otherwise. + * @throws DataGridConnectionRefusedException + */ + public boolean updateOwnership(DataGridUser group, Map addCollectionsToOwn, Map removeCollectionsToOwn) + throws DataGridConnectionRefusedException; + + /** + * Lists all user types available in the data grid + * + * @return List of all user types + */ + public List listUserTypes(); + + /** + * Removes access permission on a set of paths for a particular user. + * + * @param user + * user who permission will be removed + * @param paths + * paths to collections/data objects on which the user will lose access + * @param recursive + * flag that says whether or not the permission is applied recursively + * @throws JargonException + * @throws DataGridConnectionRefusedException + */ + void removeAccessPermissionForUserAsAdmin(DataGridUser user, Map paths) throws JargonException, + DataGridConnectionRefusedException; + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ZipService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ZipService.java new file mode 100755 index 000000000..ec4bc68f7 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ZipService.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import java.io.File; + +/** + * Interface for zipping files. + */ +public interface ZipService { + /** + * Zips a directory inside another. + * @param directoryToPlaceZip directory where the zip file will be placed + * @param directoryToZip directory to be zipped + * @return File that represents a compressed zip + */ + File createZip(File directoryToPlaceZip, File directoryToZip); +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ZoneService.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ZoneService.java new file mode 100755 index 000000000..1becd42cf --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/interfaces/ZoneService.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.interfaces; + +import com.emc.metalnx.core.domain.entity.DataGridZone; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; + +import java.util.List; + +public interface ZoneService { + + /** + * Finds all zones existing in the data grid + * + * @return List of zones + * @throws DataGridConnectionRefusedException + */ + public List findAll() throws DataGridConnectionRefusedException; +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/AdminServicesImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/AdminServicesImpl.java new file mode 100755 index 000000000..31fa3e85c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/AdminServicesImpl.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.AdminServices; +import com.emc.metalnx.services.interfaces.ConfigService; +import org.irods.jargon.core.connection.AuthScheme; +import org.irods.jargon.core.connection.IRODSAccount; +import org.irods.jargon.core.connection.auth.AuthResponse; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import java.net.ConnectException; + +@Service +@Transactional +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES) +public class AdminServicesImpl implements AdminServices { + + @Autowired + IRODSAccessObjectFactory irodsAccessObjectFactory; + + @Autowired + private ConfigService configService; + + private IRODSAccount irodsAccount; + + private static final Logger logger = LoggerFactory.getLogger(AdminServicesImpl.class); + + @Override + public UserAO getUserAO() throws DataGridConnectionRefusedException { + try { + authenticateIRODSAccount(); + + // Returning UserAO instance + return irodsAccessObjectFactory.getUserAO(irodsAccount); + + } + catch (JargonException e) { + logger.error("Could not instantiate UserAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public SpecificQueryAO getSpecificQueryAO() throws DataGridConnectionRefusedException { + try { + authenticateIRODSAccount(); + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getSpecificQueryAO(irodsAccount); + + } + catch (JargonException e) { + logger.error("Could not instantiate CollectionAndDataObjectListAndSearchAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public DataObjectAO getDataObjectAO() throws DataGridConnectionRefusedException { + try { + authenticateIRODSAccount(); + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getDataObjectAO(irodsAccount); + + } + catch (JargonException e) { + logger.error("Could not instantiate CollectionAndDataObjectListAndSearchAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return null; + } + + private void authenticateIRODSAccount() throws JargonException { + + String host = configService.getIrodsHost(); + int port = Integer.parseInt(configService.getIrodsPort()); + String zone = configService.getIrodsZone(); + String user = configService.getIrodsJobUser(); + String password = configService.getIrodsJobPassword(); + String authScheme = configService.getIrodsAuthScheme(); + String resc = "demoResc"; + String homeDir = ""; + + if (irodsAccount == null) { + IRODSAccount tempAccount = IRODSAccount.instance(host, port, user, password, homeDir, zone, resc); + tempAccount.setAuthenticationScheme(AuthScheme.findTypeByString(authScheme)); + + AuthResponse authResponse = irodsAccessObjectFactory.authenticateIRODSAccount(tempAccount); + + if (authResponse.isSuccessful()) { + irodsAccount = authResponse.getAuthenticatedIRODSAccount(); + } + } + } + + @Override + public CollectionAndDataObjectListAndSearchAO getCollectionAndDataObjectListAndSearchAO() throws DataGridConnectionRefusedException { + try { + authenticateIRODSAccount(); + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getCollectionAndDataObjectListAndSearchAO(irodsAccount); + + } + catch (JargonException e) { + logger.error("Could not instantiate CollectionAndDataObjectListAndSearchAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return null; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/CollectionServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/CollectionServiceImpl.java new file mode 100755 index 000000000..08c59b498 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/CollectionServiceImpl.java @@ -0,0 +1,1407 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.irods.jargon.core.exception.DataNotFoundException; +import org.irods.jargon.core.exception.DuplicateDataException; +import org.irods.jargon.core.exception.FileNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.protovalues.FilePermissionEnum; +import org.irods.jargon.core.pub.BulkFileOperationsAO; +import org.irods.jargon.core.pub.CollectionAO; +import org.irods.jargon.core.pub.CollectionAndDataObjectListAndSearchAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.IRODSFileSystemAO; +import org.irods.jargon.core.pub.SpecificQueryAO; +import org.irods.jargon.core.pub.domain.ClientHints; +import org.irods.jargon.core.pub.domain.Collection; +import org.irods.jargon.core.pub.domain.DataObject; +import org.irods.jargon.core.pub.domain.SpecificQueryDefinition; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.irods.jargon.core.query.CollectionAndDataObjectListingEntry; +import org.irods.jargon.core.query.CollectionAndDataObjectListingEntry.ObjectType; +import org.irods.jargon.core.query.JargonQueryException; +import org.irods.jargon.core.query.SpecificQuery; +import org.irods.jargon.core.query.SpecificQueryResultSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridFilePermission; +import com.emc.metalnx.core.domain.entity.DataGridGroupPermission; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridUserPermission; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridDataNotFoundException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridInvalidPathException; +import com.emc.metalnx.core.domain.exceptions.DataGridQueryException; +import com.emc.metalnx.core.domain.exceptions.UnsupportedDataGridFeatureException; +import com.emc.metalnx.services.interfaces.AdminServices; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.PermissionsService; +import com.emc.metalnx.services.interfaces.ResourceService; +import com.emc.metalnx.services.irods.utils.SpecificQueryProvider; +import com.emc.metalnx.services.irods.utils.SpecificQueryProviderFactory; +import com.emc.metalnx.services.irods.utils.SpecificQueryProviderFactoryImpl; +import com.emc.metalnx.services.machine.util.DataGridUtils; + +@Service +@Transactional +public class CollectionServiceImpl implements CollectionService { + + private static final String IRODS_PATH_SEPARATOR = "/"; + private static final String SQL_LIST_COLLS_MATCHING_SEARCH_TEXT_ALIAS_WITH_ORDERING = "metalnxListOfCollectionsThatMatchSearchTextWithOrdering"; + private static final String SQL_LIST_DATA_OBJECTS_MATCHING_SEARCH_TEXT_ALIAS_WITH_ORDERING = "metalnxListOfDataObjectsThatMatchSearchTextWithOrdering"; + private static final String SQL_TOTAL_NUMBER_OF_DATA_OBJECTS_MATCHING_SEARCH_TEXT_ALIAS = "metalnxTotalNumberOfDataObjectsThatMatchSearchText"; + private static final String SQL_TOTAL_NUMBER_OF_COLLS_MATCHING_SEARCH_TEXT_ALIAS = "metalnxTotalNumberOfCollectionsThatMatchSearchText"; + private static final int MAX_RESULTS_PER_PAGE = 200; + private static final Logger logger = LoggerFactory.getLogger(CollectionServiceImpl.class); + + private SpecificQueryProviderFactory specificQueryProviderFactory = new SpecificQueryProviderFactoryImpl(); + + @Autowired + AdminServices adminServices; + @Autowired + IRODSServices irodsServices; + @Autowired + ResourceService resourceService; + @Autowired + PermissionsService permissionsService; + @Autowired + FileOperationService fileOperationService; + + @Override + public boolean isFileInCollection(String filename, String collectionPath) + throws DataGridConnectionRefusedException, JargonException { + logger.info("isFileInCollection()"); + if (filename == null || collectionPath == null) + return false; + + List items = getSubCollectionsAndDataObjectsUnderPath(collectionPath); + + for (DataGridCollectionAndDataObject i : items) { + if (!i.isCollection() && filename.equals(i.getName())) + return true; + } + + return false; + } + + @Override + public boolean isPathValid(String path) { + logger.info("isPathValid()"); + + boolean isValid = false; + + try { + isValid = isCollection(path) || isDataObject(path); + } catch (DataGridException e) { + logger.error("Invalid path {}: {}", path, e.getMessage()); + } + + return isValid; + } + + @Override + public boolean isCollection(String path) throws DataGridException { + logger.info("isCollection()"); + + if (path == null || path.isEmpty()) + return false; + + IRODSFileFactory irodsFileFactory; + IRODSFile file; + boolean isColl = false; + + try { + irodsFileFactory = irodsServices.getIRODSFileFactory(); + file = irodsFileFactory.instanceIRODSFile(path); + isColl = file.isDirectory(); + } catch (JargonException e) { + logger.error("Could not check if {} is a collection: {}", path, e.getMessage()); + } catch (IllegalArgumentException e) { + logger.error("Invalid path for collection: {}", path, e.getMessage()); + throw new DataGridInvalidPathException(); + } + + return isColl; + } + + @Override + public boolean isDataObject(String path) throws DataGridException { + logger.info("isDataObject()"); + + if (path == null || path.isEmpty()) + return false; + + IRODSFileFactory irodsFileFactory; + IRODSFile file; + boolean isFile = false; + + try { + irodsFileFactory = irodsServices.getIRODSFileFactory(); + file = irodsFileFactory.instanceIRODSFile(path); + isFile = file.isFile(); + } catch (JargonException e) { + logger.error("Could not check if {} is data object: {}", path, e.getMessage()); + } catch (IllegalArgumentException e) { + logger.error("Invalid path for data object: {}", path, e.getMessage()); + throw new DataGridInvalidPathException(); + } + + return isFile; + } + + @Override + public List getSubCollectionsAndDataObjectsUnderPathThatMatchSearchTextPaginated( + String parentPath, String searchText, int pageNum, int pageSize, int orderColumn, String orderDir, + DataGridPageContext pageContext) + throws DataGridDataNotFoundException, DataGridQueryException, DataGridException { + + logger.info("getSubCollectionsAndDataObjectsUnderPathThatMatchSearchTextPaginated()"); + + List dataGridCollectionAndDataObjects = new ArrayList<>(); + + List dataGridCollections = null; + List dataGridObjects = null; + + int totalCollections = 0; + int totalDataObjects = 0; + int startIndex = (pageNum - 1) * pageSize; + int endIndex = pageNum * pageSize - 1; + int endIndexForDataObjs; + int endIndexForCollections; + + try { + totalCollections = getTotalCollectionsUnderPathThatMatchSearchText(parentPath, searchText); + totalDataObjects = getTotalDataObjectsUnderPathThatMatchSearchText(parentPath, searchText); + + pageContext.setStartItemNumber(startIndex + 1); + pageContext.setTotalNumberOfItems(totalCollections + totalDataObjects); + + if (endIndex + 1 <= totalCollections) { + dataGridCollections = listCollectionsUnderPathThatMatchSearchText(parentPath, searchText, startIndex, + pageSize, orderColumn, orderDir); + int collectionEntriesSize = dataGridCollections.size(); + + endIndexForCollections = collectionEntriesSize; + + dataGridCollectionAndDataObjects = dataGridCollections.subList(0, endIndexForCollections); + + pageContext.setEndItemNumber(pageContext.getStartItemNumber() + endIndexForCollections - 1); + } else if (startIndex + 1 > totalCollections) { + dataGridObjects = listDataObjectsUnderPathThatMatchSearchText(parentPath, searchText, + startIndex - totalCollections, pageSize, orderColumn, orderDir); + pageContext.setEndItemNumber(pageContext.getStartItemNumber() + dataGridObjects.size() - 1); + + dataGridCollectionAndDataObjects.addAll(dataGridObjects); + } else { + dataGridCollections = listCollectionsUnderPathThatMatchSearchText(parentPath, searchText, startIndex, + pageSize, orderColumn, orderDir); + endIndexForDataObjs = pageSize - totalCollections % pageSize; + dataGridObjects = listDataObjectsUnderPathThatMatchSearchText(parentPath, searchText, 0, + endIndexForDataObjs, orderColumn, orderDir); + + endIndexForDataObjs = endIndexForDataObjs > dataGridObjects.size() ? dataGridObjects.size() + : endIndexForDataObjs; + + dataGridCollectionAndDataObjects.addAll(dataGridCollections); + dataGridCollectionAndDataObjects.addAll(dataGridObjects); + + pageContext.setEndItemNumber( + pageContext.getStartItemNumber() + endIndexForDataObjs + dataGridCollections.size() - 1); + } + } catch (DataNotFoundException e) { + logger.error("Could not find items under path: {}", e.getMessage()); + throw new DataGridDataNotFoundException(e.getMessage()); + } catch (JargonQueryException e) { + logger.error("Could not query catalog for items under path: {}", e.getMessage()); + throw new DataGridQueryException(e.getMessage()); + } + return dataGridCollectionAndDataObjects; + } + + @Override + public String getPermissionsForPath(String path) throws DataGridConnectionRefusedException { + + logger.info("getPermissionsForPath()"); + + FilePermissionEnum filePermissionEnum = null; + String permissionType = "none"; + + try { + String user = irodsServices.getCurrentUser(); + String zone = irodsServices.getCurrentUserZone(); + Object obj = irodsServices.getCollectionAndDataObjectListAndSearchAO().getFullObjectForType(path); + if (obj instanceof Collection) { + CollectionAO collectionAO = irodsServices.getCollectionAO(); + filePermissionEnum = collectionAO.getPermissionForCollection(path, user, zone); + } else { + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + filePermissionEnum = dataObjectAO.getPermissionForDataObject(path, user, zone); + } + + if (filePermissionEnum != null) { + permissionType = filePermissionEnum.toString().toLowerCase(); + } + + } catch (FileNotFoundException e) { + logger.error("Could not find path: {}", e.getMessage()); + } catch (JargonException e) { + logger.error("Could not get permission for path: {}", e.getMessage()); + } + + return permissionType; + } + + @Override + public int getTotalNumberOfReplsForDataObject(String path) throws DataGridException { + + logger.info("getTotalNumberOfReplsForDataObject()"); + + int totalNumberOfRepls = 0; + + if (path == null || path.isEmpty()) { + return 0; + } + + String parentPath = path.substring(0, path.lastIndexOf(IRODS_PATH_SEPARATOR)); + String dataObjectName = path.substring(path.lastIndexOf(IRODS_PATH_SEPARATOR), path.length()); + + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + + // getting total number of replicas + try { + totalNumberOfRepls = dataObjectAO.getTotalNumberOfReplsForDataObject(parentPath, dataObjectName); + } catch (JargonException e) { + logger.error("Could not get number of replicas of a data obj: {}", e.getMessage()); + throw new DataGridException(e.getMessage()); + } + + return totalNumberOfRepls; + } + + @Override + public Map listReplicasByResource(String path) + throws DataGridConnectionRefusedException { + + logger.info("listReplicasByResource()"); + + logger.info("Listing all replicas of " + path); + Map map = new HashMap(); + + String collectionAbsPath = null; + String fileName = null; + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + + try { + collectionAbsPath = path.substring(0, path.lastIndexOf(IRODS_PATH_SEPARATOR)); + fileName = path.substring(path.lastIndexOf(IRODS_PATH_SEPARATOR) + 1, path.length()); + List replicas = dataObjectAO.listReplicationsForFile(collectionAbsPath, fileName); + + for (DataObject replica : replicas) { + DataGridCollectionAndDataObject dataGridCollectionAndDataObject = DataGridUtils + .getDataGridCollectionAndDataObject(replica); + DataGridResource dataGridResource = resourceService.find(replica.getResourceName()); + map.put(dataGridCollectionAndDataObject, dataGridResource); + } + } catch (JargonException e) { + logger.error("Could not list replicas by resource for " + path); + } + + return DataGridUtils.sortReplicaResourceMap(map); + } + + @Override + public List getSubCollectionsAndDataObjectsUnderPath(String parent) + throws DataGridConnectionRefusedException, JargonException { + + logger.info("getSubCollectionsAndDataObjectsUnderPath()"); + + CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsServices + .getCollectionAndDataObjectListAndSearchAO(); + List dataGridCollectionAndDataObjects = null; + + try { + List collectionAndDataObjects = collectionAndDataObjectListAndSearchAO + .listDataObjectsAndCollectionsUnderPath(parent); + dataGridCollectionAndDataObjects = new ArrayList(); + dataGridCollectionAndDataObjects = this + .mapListingEntryToDataGridCollectionAndDataObject(collectionAndDataObjects); + return dataGridCollectionAndDataObjects; + + } catch (FileNotFoundException e) { + logger.error("Could not locate file: ", e); + throw e; + } catch (JargonException e) { + logger.error("Error: ", e); + throw e; + } + + } + + @Override + public List getSubCollectionsUnderPath(String parent) + throws DataGridConnectionRefusedException { + + logger.info("getSubCollectionsUnderPath()"); + + CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsServices + .getCollectionAndDataObjectListAndSearchAO(); + List dataGridCollectionAndDataObjects = null; + + try { + List collectionAndDataObjects = collectionAndDataObjectListAndSearchAO + .listCollectionsUnderPath(parent, 0); + dataGridCollectionAndDataObjects = this + .mapListingEntryToDataGridCollectionAndDataObject(collectionAndDataObjects); + return dataGridCollectionAndDataObjects; + + } catch (FileNotFoundException e) { + logger.error("Could not locate file: ", e); + } catch (JargonException e) { + logger.error("Error: ", e); + } + + return dataGridCollectionAndDataObjects; + } + + @Override + public boolean createCollection(DataGridCollectionAndDataObject collection) throws DataGridException { + + logger.info("createCollection()"); + + boolean collCreated; + + try { + IRODSFileSystemAO irodsFileSystemAO = irodsServices.getIRODSFileSystemAO(); + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + + IRODSFile newFile = irodsFileFactory.instanceIRODSFile(collection.getParentPath(), collection.getName()); + irodsFileSystemAO.mkdir(newFile, false); + + // Updating inheritance option on the collection, if needed + CollectionAO collectionAO = irodsServices.getCollectionAO(); + String zoneName = irodsFileSystemAO.getIRODSServerProperties().getRodsZone(); + // enable inheritance for this collection + if (collection.isInheritanceOption()) { + collectionAO.setAccessPermissionInherit(zoneName, collection.getPath(), false); + } + // disable inheritance for this collection + else if ("own".equals(getPermissionsForPath(collection.getParentPath()))) { + collectionAO.setAccessPermissionToNotInherit(zoneName, collection.getPath(), false); + } + collCreated = true; + } catch (JargonException e) { + logger.debug("Could not create a collection in the data grid: {}", e.getMessage()); + throw new DataGridException(e.getMessage()); + } + + return collCreated; + + } + + @Override + public List searchCollectionAndDataObjectsByName(String collectionName) + throws DataGridConnectionRefusedException { + + logger.info("searchCollectionAndDataObjectsByName()"); + + CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsServices + .getCollectionAndDataObjectListAndSearchAO(); + List collectionAndDataObjects = null; + List dataGridCollectionAndDataObjects = null; + + try { + collectionAndDataObjects = collectionAndDataObjectListAndSearchAO + .searchCollectionsAndDataObjectsBasedOnName(collectionName); + dataGridCollectionAndDataObjects = this + .mapListingEntryToDataGridCollectionAndDataObject(collectionAndDataObjects); + return dataGridCollectionAndDataObjects; + } catch (JargonException e) { + logger.error("Could not search collections: {}", e.getMessage()); + } + return null; + } + + @Override + public DataGridCollectionAndDataObject findByName(String path) throws DataGridException { + + logger.info("findByName()"); + + if (path == null) { + logger.info("Could not find collection or data object by name: path is null"); + return null; + } + + logger.info("Find collection or data object by name: {}", path); + + CollectionAndDataObjectListAndSearchAO objectsAO = irodsServices.getCollectionAndDataObjectListAndSearchAO(); + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + DataGridCollectionAndDataObject dataGridCollectionAndDataObject; + + try { + CollectionAndDataObjectListingEntry entry = objectsAO + .getCollectionAndDataObjectListingEntryAtGivenAbsolutePathWithHeuristicPathGuessing(path); + dataGridCollectionAndDataObject = this.mapListingEntryToDataGridCollectionAndDataObject(entry); + if (entry.isDataObject()) { + DataObject dobj = dataObjectAO.findByAbsolutePath(path); + dataGridCollectionAndDataObject.setChecksum(dobj.getChecksum()); + } + } catch (JargonException e) { + logger.debug("Could not find collection/data object by name: {}", path); + throw new DataGridException("Could not find path " + path); + } + + return dataGridCollectionAndDataObject; + } + + @Override + public boolean modifyCollectionAndDataObject(String previousPath, String newPath, boolean inheritOption) + throws DataGridConnectionRefusedException { + + logger.info("modifyCollectionAndDataObject()"); + + IRODSFileSystemAO irodsFileSystemAO = irodsServices.getIRODSFileSystemAO(); + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + CollectionAO collectionAO = irodsServices.getCollectionAO(); + + logger.debug("Modify Collection/DataObject {} to {}", previousPath, newPath); + try { + + logger.debug("Locating {} in iRODS", previousPath); + IRODSFile previousFile = irodsFileFactory.instanceIRODSFile(previousPath); + + logger.info("Creating new IRODSFile instance for {}", newPath); + IRODSFile newFile = irodsFileFactory.instanceIRODSFile(newPath); + + boolean isDirectory = irodsFileSystemAO.isDirectory(previousFile); + if (previousPath.compareTo(newPath) != 0) { + + // checking if the path given is a collection + if (isDirectory) { + logger.debug("{} is a collection", previousPath); + irodsFileSystemAO.renameDirectory(previousFile, newFile); + } + + // the path given is a data object + else { + logger.debug("{} is a data object", previousPath); + irodsFileSystemAO.renameFile(previousFile, newFile); + } + } + + // Updating inheritance option on the collection, if needed + String zoneName = irodsFileSystemAO.getIRODSServerProperties().getRodsZone(); + if (isDirectory) { + boolean isInheritanceSetForNewCollection = collectionAO + .isCollectionSetForPermissionInheritance(newPath); + + if (inheritOption != isInheritanceSetForNewCollection) { + // enable inheritance for this collection + if (inheritOption) { + logger.debug("Setting inheritance option on {}", newPath); + collectionAO.setAccessPermissionInherit(zoneName, newPath, false); + } + // disable inheritance for this collection + else { + logger.debug("Removing inheritance setting on {}", newPath); + collectionAO.setAccessPermissionToNotInherit(zoneName, newPath, false); + } + } + } + return true; + } catch (JargonException e) { + logger.error("Could not edit Collection/DataObject {} to {}: ", previousPath, newPath, e); + } + return false; + } + + @Override + public Set listReadPermissionsForPathAndUser(String path, String userName) + throws DataGridConnectionRefusedException { + return listCollectionsWithPermissionsForUser(path, userName, FilePermissionEnum.READ); + } + + @Override + public Set listWritePermissionsForPathAndUser(String path, String userName) + throws DataGridConnectionRefusedException { + return listCollectionsWithPermissionsForUser(path, userName, FilePermissionEnum.WRITE); + } + + @Override + public Set listOwnershipForPathAndUser(String path, String userName) + throws DataGridConnectionRefusedException { + return listCollectionsWithPermissionsForUser(path, userName, FilePermissionEnum.OWN); + } + + @Override + public Set listReadPermissionsForPathAndGroup(String path, String groupName) + throws DataGridConnectionRefusedException { + return listCollectionsWithPermissionsForGroup(path, groupName, FilePermissionEnum.READ); + } + + @Override + public Set listWritePermissionsForPathAndGroup(String path, String groupName) + throws DataGridConnectionRefusedException { + return listCollectionsWithPermissionsForGroup(path, groupName, FilePermissionEnum.WRITE); + } + + @Override + public Set listOwnershipForPathAndGroup(String path, String groupName) + throws DataGridConnectionRefusedException { + return listCollectionsWithPermissionsForGroup(path, groupName, FilePermissionEnum.OWN); + } + + @Override + public Set listInheritanceForPath(String path) throws DataGridConnectionRefusedException { + + logger.info("listInheritanceForPath()"); + + Set collections = new HashSet(); + + CollectionAO collectionAO = irodsServices.getCollectionAO(); + CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsServices + .getCollectionAndDataObjectListAndSearchAO(); + + try { + List collList = collectionAndDataObjectListAndSearchAO + .listCollectionsUnderPathWithPermissions(path, 0); + for (CollectionAndDataObjectListingEntry collEntry : collList) { + String currentCollPath = collEntry.getPathOrName(); + if (collectionAO.isCollectionSetForPermissionInheritance(currentCollPath)) { + collections.add(currentCollPath); + } + } + return collections; + } catch (JargonException e) { + logger.error("Could not get collections with inheritance option enabled: ", e); + } + return null; + } + + @Override + public boolean updateInheritanceOptions(Map toAdd, Map toRemove, String zoneName) + throws DataGridConnectionRefusedException { + + logger.info("updateInheritanceOptions()"); + + CollectionAO collectionAO = irodsServices.getCollectionAO(); + try { + for (String addColl : toAdd.keySet()) { + collectionAO.setAccessPermissionInheritAsAdmin(zoneName, addColl, toAdd.get(addColl)); + } + for (String removeColl : toRemove.keySet()) { + collectionAO.setAccessPermissionToNotInheritInAdminMode(zoneName, removeColl, toRemove.get(removeColl)); + } + + return true; + } catch (JargonException e) { + logger.error("Could not set inheritance: ", e); + } + + return false; + } + + @Override + public int countAll() throws DataGridConnectionRefusedException { + logger.info("countAll()"); + + CollectionAO collectionAO = irodsServices.getCollectionAO(); + + try { + return collectionAO.countAllFilesUnderneathTheGivenCollection(IRODS_PATH_SEPARATOR); + } catch (JargonException e) { + logger.error("Could not count all files in the data grid: ", e); + } + + return 0; + } + + @Override + public Set listWritePermissionsForPathAndGroupRecursive(String path, String groupName) + throws DataGridConnectionRefusedException, JargonException { + + logger.info("listWritePermissionsForPathAndGroupRecursive()"); + + CollectionAO collectionAO = irodsServices.getCollectionAO(); + List children = getSubCollectionsAndDataObjectsUnderPath(path); + Set list = new HashSet(); + + for (DataGridCollectionAndDataObject child : children) { + try { + if (collectionAO.getPermissionForUserName(child.getPath(), groupName) != null) { + list.add(child.getPath()); + } else { + if (listWritePermissionsForPathAndGroupRecursive(child.getPath(), groupName).isEmpty()) { + list.add(child.getPath()); + } + } + } catch (JargonException e) { + logger.error("Could not list write permissions for path " + path + "and group: " + groupName, e); + } + } + + return list; + } + + /* + * ***************************************************************************** + * *************** ******************************** DATA TRANSFER OPERATIONS + * ********************************** + * ***************************************************************************** + * *************** + */ + + @Override + public String prepareFilesForDownload(String[] paths) throws IOException, DataGridException { + logger.info("prepareFilesForDownload()"); + + return prepareFilesForDownload(Arrays.asList(paths)); + } + + @Override + public String prepareFilesForDownload(List sourcePaths) throws IOException, DataGridException { + logger.info("prepareFilesForDownload()"); + + logger.info("Preparing files for download"); + if (sourcePaths == null || sourcePaths.isEmpty()) { + return ""; + } + + Date currentDate = new Date(); + String tempCollectionName = "mlx_download_" + System.currentTimeMillis(); + String tempCollectionPath = getHomeDirectyForCurrentUser() + IRODS_PATH_SEPARATOR + tempCollectionName; + String filePathToBeBundled = tempCollectionPath; + String compressedFilePath = getHomeDirectyForCurrentUser() + IRODS_PATH_SEPARATOR + tempCollectionName + ".tar"; + String path = ""; + + // if a single file was selected, it will be transferred directly + // through the HTTP response + if (sourcePaths.size() == 1 && isDataObject(sourcePaths.get(0))) { + path = sourcePaths.get(0); + } + // if two or more files will be downloaded, then a compressed file needs + // to be created in order to place all these files + else { + // creating temporary collection for download + DataGridCollectionAndDataObject tempCollection = new DataGridCollectionAndDataObject(tempCollectionPath, + getHomeDirectyForCurrentUser(), true); + + tempCollection.setCreatedAt(currentDate); + tempCollection.setModifiedAt(currentDate); + tempCollection.setInheritanceOption(false); + + logger.debug("Creating temporary collection for download"); + + boolean isTempCollectionCreated = createCollection(tempCollection); + + if (isTempCollectionCreated && !sourcePaths.isEmpty()) { + logger.info("Copying files to be downloaded to the temporary collection"); + + // copying all files and collections to be downloaded to the temporary + // collection + fileOperationService.copy(sourcePaths, tempCollectionPath, false); + + // creating the compressed file (tar) into the temporary collection + logger.info("Compressing temporary collection"); + compressTempFolderIntoDataGrid(filePathToBeBundled, compressedFilePath, ""); + path = compressedFilePath; + } + } + + return path; + } + + @Override + public boolean getInheritanceOptionForCollection(String collPath) throws DataGridConnectionRefusedException { + logger.info("getInheritanceOptionForCollection()"); + + CollectionAO collectionAO = irodsServices.getCollectionAO(); + try { + return collectionAO.isCollectionSetForPermissionInheritance(collPath); + } catch (FileNotFoundException e) { + logger.error("Collection {} does not exist: {}", collPath, e.getMessage()); + } catch (JargonException e) { + logger.error("Could not retrieve inheritance option value for", collPath, e.getMessage()); + } + return false; + } + + @Override + public int getReplicationNumber(String path) throws DataGridConnectionRefusedException { + logger.info("getReplicationNumber()"); + + logger.debug("Getting replication number of " + path); + + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + DataObject dataObject = null; + + try { + dataObject = dataObjectAO.findByAbsolutePath(path); + } catch (JargonException e) { + logger.error("Could not find a data object matching " + path, e); + } + + if (dataObject != null) { + return dataObject.getDataReplicationNumber(); + } + + return 0; + } + + @Override + public String getChecksum(String path) throws DataGridConnectionRefusedException { + logger.info("getChecksum()"); + + logger.debug("Getting checksum of " + path); + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + DataObject dataObject = null; + + try { + dataObject = dataObjectAO.findByAbsolutePath(path); + } catch (JargonException e) { + logger.error("Could not find a data object matching " + path, e); + } + + if (dataObject != null) { + return dataObject.getChecksum(); + } + + return new String(); + } + + @Override + public List mapListingEntryToDataGridCollectionAndDataObject( + List entries) { + logger.info("mapListingEntryToDataGridCollectionAndDataObject()"); + + logger.debug("Mapping a CollectionAndDataObjectListingEntry list into a DataGridCollectionAndDataObject list"); + + List dataGridCollectionAndDataObjects = new ArrayList(); + for (CollectionAndDataObjectListingEntry entry : entries) { + dataGridCollectionAndDataObjects.add(mapListingEntryToDataGridCollectionAndDataObject(entry)); + } + return dataGridCollectionAndDataObjects; + } + + @Override + public DataGridCollectionAndDataObject mapListingEntryToDataGridCollectionAndDataObject( + CollectionAndDataObjectListingEntry entry) { + logger.info("mapListingEntryToDataGridCollectionAndDataObject()"); + + logger.debug("Mapping a CollectionAndDataObjectListingEntry into a " + "DataGridCollectionAndDataObject"); + + String entryPath = ""; + String entryName = ""; + DataGridCollectionAndDataObject dgObj = null; + + if (entry.isCollection()) { + entryPath = entry.getPathOrName(); + } else { + entryPath = entry.getParentPath() + IRODS_PATH_SEPARATOR + entry.getPathOrName(); + if (entry.getParentPath().compareTo(IRODS_PATH_SEPARATOR) == 0) { + entryPath = IRODS_PATH_SEPARATOR + entry.getPathOrName(); + } + } + + entryName = entry.getNodeLabelDisplayValue(); + if (entryName == null || entryName.isEmpty()) { + entryName = entryPath; + } + + dgObj = new DataGridCollectionAndDataObject(entryPath, entryName, entry.getParentPath(), entry.isCollection()); + dgObj.setCreatedAt(entry.getCreatedAt()); + dgObj.setModifiedAt(entry.getModifiedAt()); + dgObj.setOwner(entry.getOwnerName()); + dgObj.setProxy(entry.getObjectType() == ObjectType.COLLECTION_HEURISTIC_STANDIN); + return dgObj; + } + + @Override + public String getHomeDirectyForCurrentUser() throws DataGridException { + logger.debug("Getting current user's home directory"); + + String currentUser = irodsServices.getCurrentUser(); + String zone = irodsServices.getCurrentUserZone(); + + String homeDirectory = String.format("/%s/home/%s", zone, currentUser); + + // checking if home directory exists in the grid file system + if (findByName(homeDirectory) != null) { + return homeDirectory; + } + + // if a user's home directory cannot be found, set it to the root + return String.format("/%s", zone); + } + + @Override + public String getHomeDirectyForPublic() { + logger.debug("Getting public directory"); + return IRODS_PATH_SEPARATOR + irodsServices.getCurrentUserZone() + "/home/public"; + } + + /* + * ************************************************************************* + * *************************** PRIVATE METHODS ***************************** + * ************************************************************************* + */ + + /** + * Creates a tar file into iRODS based on the path given + * + * @param filePathToBeBundled + * @param bundleFilePathTobeCreated + * @param resource + * @return + * @throws DataGridConnectionRefusedException + * @throws JargonException + */ + private boolean compressTempFolderIntoDataGrid(String filePathToBeBundled, String bundleFilePathTobeCreated, + String resource) throws DataGridConnectionRefusedException { + + logger.info("compressTempFolderIntoDataGrid()"); + + boolean isZipFileCreatedSuccessfully = false; + BulkFileOperationsAO bulkFileOperationsAO = null; + + try { + bulkFileOperationsAO = irodsServices.getBulkFileOperationsAO(); + bulkFileOperationsAO.createABundleFromIrodsFilesAndStoreInIrodsWithForceOption(bundleFilePathTobeCreated, + filePathToBeBundled, resource); + isZipFileCreatedSuccessfully = true; + } catch (JargonException e) { + logger.error("Could not compress temporary folder: {}", e.getMessage()); + } + + return isZipFileCreatedSuccessfully; + } + + /** + * Lists all collections under the given path that match a search term. Any + * collection where the term appears in the beginning, middle, and the end of + * its name will be retrieved. + * + * @param parentPath + * path to the parent collection where you are looking for items that + * match a search term + * @param searchText + * term to be matched + * @param offset + * partial start index + * @param limit + * max number of items retrieved + * @return list of data objects that match the given search text + * @throws DataGridConnectionRefusedException + */ + private List listCollectionsUnderPathThatMatchSearchText(String parentPath, + String searchText, int offset, int limit, int orderColumn, String orderDir) + throws DataGridConnectionRefusedException { + + logger.info("listCollectionsUnderPathThatMatchSearchText()"); + logger.info("parentPath:{}", parentPath); + logger.info("searchText:{}", searchText); + logger.info("offset:{}", offset); + logger.info("limit:{}", limit); + logger.info("orderColumn:{}", orderColumn); + logger.info("orderDir:{}", orderDir); + + SpecificQueryAO specificQueryAO = adminServices.getSpecificQueryAO(); + + SpecificQueryDefinition queryDef = null; + List itemsListingEntries = null; + List dataGridItemsList = null; + String sqlQueryAlias = ""; + try { + sqlQueryAlias = SQL_LIST_COLLS_MATCHING_SEARCH_TEXT_ALIAS_WITH_ORDERING; + + ClientHints clientHints = this.irodsServices.getEnvironmentalInfoAO().retrieveClientHints(false); + SpecificQueryProvider provider = specificQueryProviderFactory.instance(clientHints.whatTypeOfIcatIsIt()); + String query = provider.buildSelectCollectionsUnderPathThatMatchSearchText(parentPath, searchText, offset, + limit, orderColumn, orderDir); + + // Creating Specific Query instance + queryDef = new SpecificQueryDefinition(); + queryDef.setAlias(sqlQueryAlias); + queryDef.setSql(query); + + // Creating spec query on iRODS + specificQueryAO.addSpecificQuery(queryDef); + + // Executing specific query + String zone = irodsServices.getCurrentUserZone(); + + List args = new ArrayList(); + String collNameParam = null; + if (IRODS_PATH_SEPARATOR.equals(parentPath)) { + collNameParam = String.format("%%%s%%%%%s%%", IRODS_PATH_SEPARATOR, searchText); + } else { + collNameParam = String.format("%s%s%%%s%%", parentPath, IRODS_PATH_SEPARATOR, searchText); + } + args.add(collNameParam); + args.add(parentPath); + args.add(String.valueOf(offset)); + args.add(String.valueOf(limit)); + + SpecificQuery specQuery = SpecificQuery.instanceArguments(sqlQueryAlias, args, 0, zone); + logger.debug("specificQuery for text search:{}", specQuery); + + SpecificQueryResultSet queryResultSet = specificQueryAO.executeSpecificQueryUsingAlias(specQuery, + MAX_RESULTS_PER_PAGE, 0); + + // Mapping spec query results to DataGrid* objects + itemsListingEntries = DataGridUtils.mapCollectionQueryResultSetToDataGridObjects(queryResultSet); + dataGridItemsList = DataGridUtils.mapListingEntryToDataGridCollectionAndDataObject(itemsListingEntries); + } catch (JargonException | JargonQueryException | UnsupportedDataGridFeatureException e) { + logger.error("Could not execute specific query to find collections matching a search text. ", e); + } finally { + try { + // after running the user specific query, we need to remove from the database + specificQueryAO.removeSpecificQueryByAlias(sqlQueryAlias); + } catch (JargonException e) { + logger.error("Could not remove specific query {}: ", sqlQueryAlias, e.getMessage()); + } + } + + return dataGridItemsList; + } + + /** + * Lists all data objects under the given path that match a search term. Any + * data object where the term appears in the beginning, middle, and the end of + * its name will be retrieved. + * + * @param parentPath + * path to the parent collection where you are looking for items that + * match a search term + * @param searchText + * term to be matched + * @param offset + * partial start index + * @param limit + * max number of items retrieved + * @return list of data objects that match the given search text + * @throws DataGridConnectionRefusedException + */ + private List listDataObjectsUnderPathThatMatchSearchText(String parentPath, + String searchText, int offset, int limit, int orderColumn, String orderDir) + throws DataNotFoundException, JargonQueryException, DataGridConnectionRefusedException { + + logger.info("listDataObjectsUnderPathThatMatchSearchText()"); + + logger.info("parentPath:{}", parentPath); + logger.info("searchText:{}", searchText); + logger.info("offset:{}", offset); + logger.info("limit:{}", limit); + logger.info("orderColumn:{}", orderColumn); + logger.info("orderDir:{}", orderDir); + + SpecificQueryAO specificQueryAO = adminServices.getSpecificQueryAO(); + + SpecificQueryDefinition queryDef = null; + List dataGridList = null; + List dataGridCollectionAndDataObjects = null; + String sqlAlias = null; + + try { + dataGridCollectionAndDataObjects = new ArrayList(); + sqlAlias = SQL_LIST_DATA_OBJECTS_MATCHING_SEARCH_TEXT_ALIAS_WITH_ORDERING; + + ClientHints clientHints = this.irodsServices.getEnvironmentalInfoAO().retrieveClientHints(false); + SpecificQueryProvider provider = specificQueryProviderFactory.instance(clientHints.whatTypeOfIcatIsIt()); + String query = provider.buildSelectDataObjectsUnderPathThatMatchSearchText(parentPath, searchText, offset, + limit, orderColumn, orderDir); + + // Creating Specific Query instance + queryDef = new SpecificQueryDefinition(); + queryDef.setAlias(sqlAlias); + queryDef.setSql(query.toString()); + + // Creating spec query on iRODS + specificQueryAO.addSpecificQuery(queryDef); + + // Executing specific query + String zone = irodsServices.getCurrentUserZone(); + + List args = new ArrayList(); + args.add(parentPath); + args.add("%" + searchText + "%"); + args.add(String.valueOf(offset)); + args.add(String.valueOf(limit)); + + SpecificQuery specQuery = SpecificQuery.instanceArguments(sqlAlias, args, 0, zone); + SpecificQueryResultSet queryResultSet = specificQueryAO.executeSpecificQueryUsingAlias(specQuery, + MAX_RESULTS_PER_PAGE, 0); + + // Mapping spec query results to DataGrid* objects + dataGridList = DataGridUtils.mapQueryResultSetToDataGridObjectsForSearch(queryResultSet); + dataGridCollectionAndDataObjects + .addAll(DataGridUtils.mapListingEntryToDataGridCollectionAndDataObject(dataGridList)); + } catch (JargonException | UnsupportedDataGridFeatureException e) { + logger.error("Could not execute specific query for listing data objects that match a search text", e); + } finally { + try { + // after running the user specific query, we need to remove from the database + specificQueryAO.removeSpecificQueryByAlias(sqlAlias); + } catch (JargonException e) { + logger.error("Could not remove specific query {}: ", sqlAlias, e.getMessage()); + } + } + + return dataGridCollectionAndDataObjects; + } + + /** + * Calculates the number of collections that match the given search term. + * + * @param parentPath + * path to the parent collection where you are looking for items that + * match a search term + * @param searchText + * term to be matched + * @return total number of collections matching the given search text + * @throws DataGridConnectionRefusedException + * @throws JargonException + * @throws DuplicateDataException + * @throws JargonQueryException + */ + private int getTotalCollectionsUnderPathThatMatchSearchText(String parentPath, String searchText) + throws DataGridConnectionRefusedException { + + logger.info("getTotalCollectionsUnderPathThatMatchSearchText()"); + + SpecificQueryAO specificQueryAO = adminServices.getSpecificQueryAO(); + + int totalNumberOfItems = 0; + SpecificQueryDefinition queryDef = null; + String sqlQueryAlias = "totalNumberOfCollectionsThatMatchSearchText"; + + try { + sqlQueryAlias = SQL_TOTAL_NUMBER_OF_COLLS_MATCHING_SEARCH_TEXT_ALIAS; + + ClientHints clientHints = this.irodsServices.getEnvironmentalInfoAO().retrieveClientHints(false); + SpecificQueryProvider provider = specificQueryProviderFactory.instance(clientHints.whatTypeOfIcatIsIt()); + String query = provider.buildSelectTotalCollectionsUnderPathThatMatchSearchText(parentPath, searchText); + + // Creating Specific Query instance + queryDef = new SpecificQueryDefinition(); + queryDef.setAlias(sqlQueryAlias); + queryDef.setSql(query); + + // Creating spec query on iRODS + specificQueryAO.addSpecificQuery(queryDef); + + // Executing specific query + String zone = irodsServices.getCurrentUserZone(); + + List args = new ArrayList(); + String collNameParam = null; + if (IRODS_PATH_SEPARATOR.equals(parentPath)) { + collNameParam = String.format("%%%s%%%%%s%%", IRODS_PATH_SEPARATOR, searchText); + } else { + collNameParam = String.format("%s%s%%%s%%", parentPath, IRODS_PATH_SEPARATOR, searchText); + } + args.add(collNameParam); + args.add(parentPath); + + SpecificQuery specQuery = SpecificQuery.instanceArguments(sqlQueryAlias, args, 0, zone); + SpecificQueryResultSet queryResultSet = specificQueryAO.executeSpecificQueryUsingAlias(specQuery, + MAX_RESULTS_PER_PAGE, 0); + totalNumberOfItems = DataGridUtils.mapCountQueryResultSetToInteger(queryResultSet); + } catch (JargonException | JargonQueryException | UnsupportedDataGridFeatureException e) { + logger.error("Could not execute specific query to find collections matching a search text. ", e); + } finally { + try { + // after running the user specific query, we need to remove from the database + specificQueryAO.removeSpecificQueryByAlias(sqlQueryAlias); + } catch (JargonException e) { + logger.error("Could not remove specific query {}: ", sqlQueryAlias, e.getMessage()); + } + } + + return totalNumberOfItems; + } + + /** + * Calculates the number of data objects that match the given search term. + * + * @param parentPath + * path to the parent collection where you are looking for items that + * match a search term + * @param searchText + * term to be matched + * @return total number of collections matching the given search text + * @throws DataGridConnectionRefusedException + */ + private int getTotalDataObjectsUnderPathThatMatchSearchText(String parentPath, String searchText) + throws DataNotFoundException, JargonQueryException, DataGridConnectionRefusedException { + + logger.info("getTotalCollectionsUnderPathThatMatchSearchText()"); + + logger.info("parentPath:{}", parentPath); + logger.info("searchText:{}", searchText); + + SpecificQueryAO specificQueryAO = adminServices.getSpecificQueryAO(); + + SpecificQueryDefinition queryDef = null; + String sqlAlias = null; + int totalNumberOfItems = 0; + + try { + sqlAlias = SQL_TOTAL_NUMBER_OF_DATA_OBJECTS_MATCHING_SEARCH_TEXT_ALIAS; + ClientHints clientHints = this.irodsServices.getEnvironmentalInfoAO().retrieveClientHints(false); + SpecificQueryProvider provider = specificQueryProviderFactory.instance(clientHints.whatTypeOfIcatIsIt()); + String query = provider.buildSelectTotalDataObjectsUnderPathThatMatchSearchText(parentPath, searchText); + + // Creating Specific Query instance + queryDef = new SpecificQueryDefinition(); + queryDef.setAlias(sqlAlias); + queryDef.setSql(query.toString()); + + // Creating spec query on iRODS + specificQueryAO.addSpecificQuery(queryDef); + + // Executing specific query + String zone = irodsServices.getCurrentUserZone(); + + List args = new ArrayList(); + args.add(parentPath); + args.add("%" + searchText + "%"); + + SpecificQuery specQuery = SpecificQuery.instanceArguments(sqlAlias, args, 0, zone); + SpecificQueryResultSet queryResultSet = specificQueryAO.executeSpecificQueryUsingAlias(specQuery, + MAX_RESULTS_PER_PAGE, 0); + + // Mapping spec query results to DataGrid* objects + totalNumberOfItems = DataGridUtils.mapCountQueryResultSetToInteger(queryResultSet); + } catch (JargonException | UnsupportedDataGridFeatureException e) { + logger.error( + "Could not execute specific query to get the total number of data objects matching a search text.", + e); + } finally { + try { + // after running the user specific query, we need to remove from the database + specificQueryAO.removeSpecificQueryByAlias(sqlAlias); + } catch (JargonException e) { + logger.error("Could not remove specific query {}: ", sqlAlias, e.getMessage()); + } + } + + return totalNumberOfItems; + } + + private Set listCollectionsWithPermissionsForUser(String path, String entityName, + FilePermissionEnum permissionType) throws DataGridConnectionRefusedException { + logger.info("listCollectionsWithPermissionsForUser()"); + + return listCollectionsWithPermissionsForEntity(path, entityName, permissionType, "user"); + } + + private Set listCollectionsWithPermissionsForGroup(String path, String entityName, + FilePermissionEnum permissionType) throws DataGridConnectionRefusedException { + logger.info("listCollectionsWithPermissionsForGroup()"); + + return listCollectionsWithPermissionsForEntity(path, entityName, permissionType, "group"); + } + + /** + * Auxiliary method that filters the directories with the specified permission + * level for the given entity, that can be an user or a group + * + * @param path + * The path for which we would like to list the objects with + * permissions + * @param entityName + * The entity name + * @param permissionType + * The permission stype + * @param entityType + * The entity type that can be user or group + * @return + * @throws DataGridConnectionRefusedException + */ + private Set listCollectionsWithPermissionsForEntity(String path, String entityName, + FilePermissionEnum permissionType, String entityType) throws DataGridConnectionRefusedException { + + logger.info("listCollectionsWithPermissionsForEntity()"); + + Set collections = new HashSet(); + + CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsServices + .getCollectionAndDataObjectListAndSearchAO(); + + try { + List collList = collectionAndDataObjectListAndSearchAO + .listDataObjectsAndCollectionsUnderPath(path); + + logger.info("Getting permissions and bookmarks for path {}", path); + for (CollectionAndDataObjectListingEntry collEntry : collList) { + String filePath = ""; + + if (collEntry.isCollection()) { + filePath = collEntry.getPathOrName(); + } else { + filePath = collEntry.getParentPath() + IRODS_PATH_SEPARATOR + collEntry.getPathOrName(); + if (collEntry.getParentPath().compareTo(IRODS_PATH_SEPARATOR) == 0) { + filePath = collEntry.getParentPath() + collEntry.getPathOrName(); + } + } + + List permissions = permissionsService.getPathPermissionDetails(filePath, + entityName); + + // Deciding whether we should get permissions for users or groups + if (entityType.compareTo("user") == 0) { + + // Filtering permissions for users + List userPermissions = permissionsService + .getUsersWithPermissions(permissions); + + // Adding collections with permissions into the result list + for (DataGridUserPermission dataGridUserPermission : userPermissions) { + if (dataGridUserPermission.getPermission().equalsIgnoreCase(permissionType.name())) { + collections.add(filePath); + } + } + } else { + // Filtering permissions for groups + List groupPermissions = permissionsService + .getGroupsWithPermissions(permissions); + + // Adding collections with permissions into the result list + for (DataGridGroupPermission dataGridGroupPermission : groupPermissions) { + if (dataGridGroupPermission.getPermission().equalsIgnoreCase(permissionType.name())) { + collections.add(filePath); + } + } + } + } + } catch (JargonException e) { + logger.error("Could not get permissions: ", e); + } + return collections; + } + + @Override + public String getTrashForPath(String path) { + Pattern pattern = Pattern.compile("^/(\\w+)/trash/home/(\\w+)"); + Matcher matcher = pattern.matcher(path); + if (matcher.find()) { + return matcher.group(0); + } + + // if trash for path above is not found, user trash collection is used instead + return String.format("/%s/trash/home/%s", irodsServices.getCurrentUserZone(), irodsServices.getCurrentUser()); + } + + /** + * @return the adminServices + */ + public AdminServices getAdminServices() { + return adminServices; + } + + /** + * @param adminServices + * the adminServices to set + */ + public void setAdminServices(AdminServices adminServices) { + this.adminServices = adminServices; + } + + /** + * @return the irodsServices + */ + public IRODSServices getIrodsServices() { + return irodsServices; + } + + /** + * @param irodsServices + * the irodsServices to set + */ + public void setIrodsServices(IRODSServices irodsServices) { + this.irodsServices = irodsServices; + } + + /** + * @return the resourceService + */ + public ResourceService getResourceService() { + return resourceService; + } + + /** + * @param resourceService + * the resourceService to set + */ + public void setResourceService(ResourceService resourceService) { + this.resourceService = resourceService; + } + + /** + * @return the permissionsService + */ + public PermissionsService getPermissionsService() { + return permissionsService; + } + + /** + * @param permissionsService + * the permissionsService to set + */ + public void setPermissionsService(PermissionsService permissionsService) { + this.permissionsService = permissionsService; + } + + /** + * @return the fileOperationService + */ + public FileOperationService getFileOperationService() { + return fileOperationService; + } + + /** + * @param fileOperationService + * the fileOperationService to set + */ + public void setFileOperationService(FileOperationService fileOperationService) { + this.fileOperationService = fileOperationService; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/FavoritesServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/FavoritesServiceImpl.java new file mode 100755 index 000000000..535723634 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/FavoritesServiceImpl.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.dao.FavoriteDao; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserFavorite; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FavoritesService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +@Service +@Transactional +public class FavoritesServiceImpl implements FavoritesService { + + @Autowired + IRODSServices irodsServices; + + @Autowired + CollectionService collectionService; + + @Autowired + FavoriteDao favoriteDao; + + private static final Logger logger = LoggerFactory.getLogger(FavoritesServiceImpl.class); + + /** + * Updates the favorites table for a user, whether be it to remove or add a path + * + * @param user + * @param set + * of paths to be added + * @param set + * of paths to be removed + * @return True, if operation is successful. False, otherwise. + */ + @Override + public boolean updateFavorites(DataGridUser user, Set toAdd, Set toRemove) { + + boolean operationResult = true; + + try { + if (toAdd != null) { + for (String path : toAdd) { + if (!findFavoritesForUserAsString(user).contains(path)) { + favoriteDao.addByUserAndPath(user, path, collectionService.isCollection(path)); + } + } + } + + if (toRemove != null) { + for (String path : toRemove) { + favoriteDao.removeByUserAndPath(user, path); + } + } + } + catch (Exception e) { + operationResult = false; + logger.error("Could not modify favorite for {}", user.getUsername(), e); + } + + return operationResult; + } + + /** + * Returns a list of strings with each of them representing a path marked as favorite + * + * @param user + * @return List of paths marked as favorites by the user. + */ + @Override + public List findFavoritesForUserAsString(DataGridUser user) { + List favorites = favoriteDao.findByUser(user); + List strings = new ArrayList(); + + for (DataGridUserFavorite favorite : favorites) { + strings.add(favorite.getPath()); + } + + return strings; + } + + @Override + public List findFavoritesPaginated(DataGridUser user, int offset, int limit, String searchString, String orderBy, + String orderDir, boolean onlyCollections) { + List favorites = favoriteDao.findByUserPaginated(user, offset, limit, searchString, orderBy, orderDir, onlyCollections); + return favorites; + } + + /** + * Removes path from database. This operation is used when the corresponding collection or file + * is deleted from the grid + * + * @param path + * @return True, if operation is successful. False, otherwise. + */ + @Override + public boolean removeFavoriteBasedOnPath(String path) { + return favoriteDao.removeByPath(path); + } + + /** + * Removes path from database. This operation is used when the corresponding collection or file + * is deleted from the grid + * + * @param path + * @return True, if operation is successful. False, otherwise. + */ + @Override + public boolean removeFavoriteBasedOnRelativePath(String path) { + return favoriteDao.removeByParentPath(path); + } + + /** + * Checks whether the parameter path is a favorite for parameter user + * + * @param user + * @param path + * @return True, if path is a favorite for user. False, otherwise. + */ + @Override + public boolean isPathFavoriteForUser(DataGridUser user, String path) { + return favoriteDao.findByUserAndPath(user, path) != null; + } + + @Override + public boolean removeFavoriteBasedOnUser(DataGridUser user) { + return favoriteDao.removeByUser(user); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/FileOperationServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/FileOperationServiceImpl.java new file mode 100755 index 000000000..a5b7a2f93 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/FileOperationServiceImpl.java @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.io.FilenameUtils; +import org.irods.jargon.core.exception.DataNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.DataTransferOperations; +import org.irods.jargon.core.pub.IRODSFileSystemAO; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.irods.jargon.core.pub.io.IRODSFileInputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.FileCopyUtils; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridChecksumException; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridReplicateException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FavoritesService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.GroupBookmarkService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.RuleService; +import com.emc.metalnx.services.interfaces.UserBookmarkService; + +@Service +@Transactional +public class FileOperationServiceImpl implements FileOperationService { + + private static final String CONTENT_TYPE = "application/octet-stream"; + private static final String HEADER_FORMAT = "attachment;filename=\"%s\""; + private static final Logger logger = LoggerFactory.getLogger(FileOperationServiceImpl.class); + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private CollectionService collectionService; + + @Autowired + private UserBookmarkService userBookmarkService; + + @Autowired + private GroupBookmarkService groupBookmarkService; + + @Autowired + private FavoritesService favoritesService; + + @Autowired + private MetadataService metadataService; + + @Autowired + private RuleService rs; + + @Override + public boolean copy(String sourcePath, String dstPath, boolean copyWithMetadata) + throws DataGridConnectionRefusedException { + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + DataTransferOperations dataTransferOperations = irodsServices.getDataTransferOperations(); + + boolean isCopied = false; + try { + IRODSFile source = irodsFileFactory.instanceIRODSFile(sourcePath); + IRODSFile target = irodsFileFactory.instanceIRODSFile(dstPath); + dataTransferOperations.copy(source, target, null, null); + isCopied = true; + + if (copyWithMetadata) { + String objName = sourcePath.substring(sourcePath.lastIndexOf("/") + 1, sourcePath.length()); + dstPath = String.format("%s/%s", dstPath, objName); + metadataService.copyMetadata(sourcePath, dstPath); + } + } catch (JargonException e) { + logger.error("Could not copy item from " + sourcePath + " to " + dstPath + ": ", e.getMessage()); + } + + return isCopied; + } + + @Override + public boolean copy(List sourcePaths, String dstPath, boolean copyWithMetadata) + throws DataGridConnectionRefusedException { + boolean isCopied = true; + + for (String sourcePath : sourcePaths) { + isCopied &= this.copy(sourcePath, dstPath, copyWithMetadata); + } + + return isCopied; + } + + @Override + public boolean deleteItem(String path, boolean force) throws DataGridConnectionRefusedException { + if (path == null || path.isEmpty()) + return false; + + IRODSFileSystemAO irodsFileSystemAO = irodsServices.getIRODSFileSystemAO(); + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + boolean itemDeleted = false; + + try { + IRODSFile itemToBeRemoved = irodsFileFactory.instanceIRODSFile(path); + + if (irodsFileSystemAO.isDirectory(itemToBeRemoved)) { + // if force set to true, we do an irm -rf + if (force) { + logger.info("Deleting directory (force) {}", path); + irodsFileSystemAO.directoryDeleteForce(itemToBeRemoved); + } + // irm + else { + logger.info("Deleting directory {}", path); + irodsFileSystemAO.directoryDeleteNoForce(itemToBeRemoved); + } + } else { + // if force set to false, we do an irm + if (force) { + logger.info("Deleting data obj (force) {}", path); + irodsFileSystemAO.fileDeleteForce(itemToBeRemoved); + } + // irm + else { + logger.info("Deleting data obj {}", path); + irodsFileSystemAO.fileDeleteNoForce(itemToBeRemoved); + } + } + + itemDeleted = true; + + // item deleted, we need to delete any bookmarks related to it + userBookmarkService.removeBookmarkBasedOnPath(path); + userBookmarkService.removeBookmarkBasedOnRelativePath(path); + + groupBookmarkService.removeBookmarkBasedOnPath(path); + groupBookmarkService.removeBookmarkBasedOnRelativePath(path); + + favoritesService.removeFavoriteBasedOnPath(path); + favoritesService.removeFavoriteBasedOnRelativePath(path); + + } catch (JargonException e) { + logger.error("Could not delete item " + path + ": ", e); + } + + return itemDeleted; + } + + @Override + public boolean deleteCollection(String collectionPath, boolean forceFlag) throws DataGridException { + IRODSFileSystemAO irodsFileSystemAO = irodsServices.getIRODSFileSystemAO(); + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + + try { + IRODSFile collectionToBeRemoved = irodsFileFactory.instanceIRODSFile(collectionPath); + + if (forceFlag) { + irodsFileSystemAO.directoryDeleteForce(collectionToBeRemoved); + } else { + irodsFileSystemAO.directoryDeleteNoForce(collectionToBeRemoved); + } + return true; + } catch (JargonException e) { + logger.error("Could not delete collection: ", e.getMessage()); + } + + return false; + } + + @Override + public boolean deleteDataObject(String dataObjectPath, boolean forceFlag) throws DataGridException { + + boolean dataObjDeleted = false; + + try { + IRODSFileSystemAO irodsFileSystemAO = irodsServices.getIRODSFileSystemAO(); + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + + IRODSFile fileToBeRemoved = irodsFileFactory.instanceIRODSFile(dataObjectPath); + if (forceFlag) { + irodsFileSystemAO.fileDeleteForce(fileToBeRemoved); + } else { + irodsFileSystemAO.fileDeleteNoForce(fileToBeRemoved); + } + + dataObjDeleted = true; + } catch (JargonException e) { + logger.error("Could not delete data object: {}", e.getMessage()); + } + + return dataObjDeleted; + } + + @Override + public boolean deleteReplica(String path, String fileName, int replicaNumber, boolean inAdminMode) + throws DataGridConnectionRefusedException { + + boolean deleteSuccess = false; + try { + String parentPath = path.substring(0, path.lastIndexOf("/")); + irodsServices.getDataObjectAO().trimDataObjectReplicas(parentPath, fileName, "", -1, replicaNumber, + inAdminMode); + deleteSuccess = true; + } catch (DataNotFoundException e) { + logger.error("Data object could not be found: " + e.toString()); + } catch (JargonException e) { + logger.error("Delete replica operation failed: " + e.toString()); + } + return deleteSuccess; + } + + @Override + public boolean download(String path, HttpServletResponse response, boolean removeTempCollection) + throws DataGridException { + + logger.debug("Downloading file ", path); + + if (path == null || path.isEmpty() || response == null) { + return false; + } + + logger.debug("Copying file into the HTTP response"); + + boolean isDownloadSuccessful = copyFileIntoHttpResponse(path, response); + + String fileName = path.substring(path.lastIndexOf("/"), path.length()); + + // getting the temporary collection name from the compressed file name, + // and removing the ".tar" extension from it + String tempColl = collectionService.getHomeDirectyForCurrentUser() + + fileName.substring(0, fileName.length() - 4); + + /* + * String tempTrashColl = getTrashDirectoryForCurrentUser() + + * fileName.substring(0, fileName.length() - 4); + */ + + logger.debug("Removing compressed file"); + + // removing any temporary collections and tar files created for downloading + if (removeTempCollection) { + deleteDataObject(path, removeTempCollection); + + logger.debug("Removing temporary collection"); + + // removing temporary collection + deleteCollection(tempColl, removeTempCollection); + } + + return isDownloadSuccessful; + } + + @Override + public boolean emptyTrash(DataGridUser user, String collectionPath) throws DataGridConnectionRefusedException { + if (user == null || collectionPath == null || collectionPath.isEmpty()) + return false; + + boolean itemsDeleted = false; + + try { + String resc = irodsServices.getDefaultStorageResource(); + rs.execEmptyTrashRule(resc, collectionPath, user.isAdmin()); + itemsDeleted = true; + } catch (DataGridRuleException e) { + logger.info("Could not empty trash: ", e.getMessage()); + } + + return itemsDeleted; + } + + @Override + public boolean move(String sourcePath, String targetPath) throws DataGridConnectionRefusedException { + + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + + DataTransferOperations dataTransferOperations = irodsServices.getDataTransferOperations(); + + try { + IRODSFile source = irodsFileFactory.instanceIRODSFile(sourcePath); + + if (source.isDirectory()) { + targetPath += "/" + FilenameUtils.getBaseName(sourcePath); + } + + IRODSFile target = irodsFileFactory.instanceIRODSFile(targetPath); + + dataTransferOperations.move(source, target); + + return true; + } catch (JargonException e) { + logger.error("Could not move item from " + sourcePath + " to " + targetPath + ": ", e.getMessage()); + } + + return false; + } + + @Override + public void replicateDataObject(String path, String targetResource, boolean inAdminMode) + throws DataGridReplicateException, DataGridConnectionRefusedException { + logger.info("Replicating {} into the resource {} [admin mode: {}]", path, targetResource, inAdminMode); + + try { + rs.execReplDataObjRule(targetResource, path, inAdminMode); + } catch (DataGridRuleException e) { + logger.info("File replication failed ({}) into the resource {} [admin mode: {}]", path, targetResource, + inAdminMode); + throw new DataGridReplicateException("File replication failed."); + } + } + + @Override + public void computeChecksum(String path, String filename) + throws DataGridChecksumException, DataGridConnectionRefusedException { + if (path == null || path.isEmpty() || filename == null || filename.isEmpty()) + throw new DataGridChecksumException("Could not calculate checksum. File path is invalid."); + + logger.info("Computing checksum for {} ({})", filename, path); + + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + IRODSFile file; + + try { + file = irodsFileFactory.instanceIRODSFile(path, filename); + dataObjectAO.computeMD5ChecksumOnDataObject(file); + } catch (JargonException e) { + logger.error("Could not calculate checksum: {}", e.getMessage()); + throw new DataGridChecksumException("Could not calculate checksum."); + } + } + + /** + * Copies a buffered input stream from a file to a HTTP response for + * downloading. + * + * @param path + * path to the file in iRODS to be added to the HTTP response + * @param response + * HTTP response to let the user download the file + * @return True, if the file was successfully added to the HTTP response. False, + * otherwise. + * @throws DataGridConnectionRefusedException + * is Metalnx cannot connect to the data grid + */ + private boolean copyFileIntoHttpResponse(String path, HttpServletResponse response) + throws DataGridConnectionRefusedException { + + boolean isCopySuccessFul = true; + IRODSFileInputStream irodsFileInputStream = null; + IRODSFile irodsFile = null; + + logger.debug("Trying to copy path stream {} to user", path); + + try { + String fileName = path.substring(path.lastIndexOf("/") + 1, path.length()); + logger.debug("The filename is [{}]", fileName); + + logger.debug("Initiating iRodsFileFactory"); + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + + logger.debug("Getting iRodsFileFactory instance for {}", path); + irodsFile = irodsFileFactory.instanceIRODSFile(path); + + logger.debug("Creating stream from {}", irodsFile); + irodsFileInputStream = irodsFileFactory.instanceIRODSFileInputStream(irodsFile); + + // set file mime type + response.setContentType(CONTENT_TYPE); + response.setHeader("Content-Disposition", String.format(HEADER_FORMAT, fileName)); + response.setContentLength((int) irodsFile.length()); + + FileCopyUtils.copy(irodsFileInputStream, response.getOutputStream()); + + } catch (IOException e) { + logger.error("Could not put the file in the Http response ", e); + isCopySuccessFul = false; + } catch (JargonException e) { + logger.error("Could not copy file in the Http response: ", e.getMessage()); + isCopySuccessFul = false; + } + + finally { + try { + if (irodsFileInputStream != null) + irodsFileInputStream.close(); + if (irodsFile != null) + irodsFile.close(); + } catch (Exception e) { + logger.error("Could not close stream(s): ", e.getMessage()); + } + } + + return isCopySuccessFul; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/FilePropertyServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/FilePropertyServiceImpl.java new file mode 100755 index 000000000..fc623571a --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/FilePropertyServiceImpl.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import java.net.ConnectException; +import java.util.ArrayList; +import java.util.List; + +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.CollectionAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.domain.UserFilePermission; +import org.irods.jargon.core.query.SpecificQueryResultSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridFilePropertySearch; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.UnsupportedDataGridFeatureException; +import com.emc.metalnx.services.interfaces.ConfigService; +import com.emc.metalnx.services.interfaces.FilePropertyService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.SpecQueryService; +import com.emc.metalnx.services.interfaces.UserService; +import com.emc.metalnx.services.machine.util.DataGridUtils; + +@Service +@Transactional +public class FilePropertyServiceImpl implements FilePropertyService { + + @Autowired + IRODSServices irodsServices; + + @Autowired + UserService userService; + + @Autowired + SpecQueryService specQueryService; + + @Autowired + private ConfigService configService; + + private static final Logger logger = LoggerFactory.getLogger(FilePropertyServiceImpl.class); + + @Override + public List findByFileProperties(List searchList, + DataGridPageContext pageContext, int pageNum, int pageSize) + throws DataGridConnectionRefusedException, JargonException { + + List dataGridCollectionAndDataObjects = null; + List dataGridObjects = null; + List dataGridCollections = null; + + int totalCollections = 0; + int totalDataObjects = 0; + int startIndex = (pageNum - 1) * pageSize; + int endIndex = (pageNum * pageSize) - 1; + int endIndexForDataObjs; + int endIndexForCollections; + + try { + String zone = irodsServices.getCurrentUserZone(); + + totalCollections = specQueryService.countCollectionsMatchingFileProperties(searchList, zone); + totalDataObjects = specQueryService.countDataObjectsMatchingFileProperties(searchList, zone); + + pageContext.setStartItemNumber(startIndex + 1); + pageContext.setTotalNumberOfItems(totalCollections + totalDataObjects); + + dataGridCollectionAndDataObjects = new ArrayList(); + + if (endIndex + 1 <= totalCollections) { + // looking for collections + SpecificQueryResultSet resultSetColls = specQueryService.searchByFileProperties(searchList, zone, true, + pageContext, startIndex, pageSize); + + dataGridCollections = DataGridUtils.mapPropertiesResultSetToDataGridObjects(resultSetColls); + + endIndexForCollections = dataGridCollections.size(); + + dataGridCollectionAndDataObjects.addAll(dataGridCollections); + + pageContext.setEndItemNumber(pageContext.getStartItemNumber() + endIndexForCollections - 1); + } else if (startIndex + 1 > totalCollections) { + // looking for data objects + SpecificQueryResultSet resultSetDataObjs = specQueryService.searchByFileProperties(searchList, zone, + false, pageContext, startIndex - totalCollections, pageSize); + + dataGridObjects = DataGridUtils.mapPropertiesResultSetToDataGridObjects(resultSetDataObjs); + + pageContext.setEndItemNumber(pageContext.getStartItemNumber() + dataGridObjects.size() - 1); + + dataGridCollectionAndDataObjects.addAll(dataGridObjects); + } else { + // looking for collections + SpecificQueryResultSet resultSetColls = specQueryService.searchByFileProperties(searchList, zone, true, + pageContext, startIndex, pageSize); + + dataGridCollections = DataGridUtils.mapPropertiesResultSetToDataGridObjects(resultSetColls); + + endIndexForDataObjs = pageSize - (totalCollections % pageSize); + + // looking for data objects + SpecificQueryResultSet resultSetDataObjs = specQueryService.searchByFileProperties(searchList, zone, + false, pageContext, 0, endIndexForDataObjs); + + dataGridObjects = DataGridUtils.mapPropertiesResultSetToDataGridObjects(resultSetDataObjs); + + endIndexForDataObjs = endIndexForDataObjs > dataGridObjects.size() ? dataGridObjects.size() + : endIndexForDataObjs; + + dataGridCollectionAndDataObjects.addAll(dataGridCollections); + dataGridCollectionAndDataObjects.addAll(dataGridObjects); + + pageContext.setEndItemNumber( + pageContext.getStartItemNumber() + endIndexForDataObjs + dataGridCollections.size() - 1); + } + + } catch (JargonException | UnsupportedDataGridFeatureException e) { + logger.error("Could not find data objects by metadata. ", e); + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(); + } + } + + this.populateVisibilityForCurrentUser(dataGridCollectionAndDataObjects); + return dataGridCollectionAndDataObjects; + } + + private void populateVisibilityForCurrentUser(List objectList) + throws DataGridConnectionRefusedException { + + CollectionAO collectionAO = irodsServices.getCollectionAO(); + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + String currentUser = getLoggedDataGridUser().getUsername(); + + for (DataGridCollectionAndDataObject obj : objectList) { + + List permissions = null; + + try { + if (obj.isCollection()) { + permissions = collectionAO.listPermissionsForCollection(obj.getPath()); + } else { + permissions = dataObjectAO.listPermissionsForDataObject(obj.getPath()); + } + } catch (JargonException e) { + logger.error("Could not get permission list for object {}", obj.getPath(), e); + } + + obj.setVisibleToCurrentUser(false); + if (permissions != null) { + for (UserFilePermission permission : permissions) { + if (permission.getUserName().compareTo(currentUser) == 0) { + obj.setVisibleToCurrentUser(true); + break; + } + } + } + + } + } + + private DataGridUser getLoggedDataGridUser() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + String username = (String) auth.getPrincipal(); + + return userService.findByUsernameAndAdditionalInfo(username, configService.getIrodsZone()); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/GroupBookmarkServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/GroupBookmarkServiceImpl.java new file mode 100755 index 000000000..f051e87b4 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/GroupBookmarkServiceImpl.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.dao.GroupBookmarkDao; +import com.emc.metalnx.core.domain.dao.GroupDao; +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridGroupBookmark; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.*; +import org.irods.jargon.core.exception.DataNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.UserAO; +import org.irods.jargon.core.pub.domain.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +@Service +@Transactional +public class GroupBookmarkServiceImpl implements GroupBookmarkService { + + @Autowired + GroupDao groupDao; + + @Autowired + GroupBookmarkDao groupBookmarkDao; + + @Autowired + IRODSServices irodsServices; + + @Autowired + AdminServices adminServices; + + @Autowired + UserService userService; + + @Autowired + GroupService groupService; + + @Autowired + CollectionService collectionService; + + private static final Logger logger = LoggerFactory.getLogger(GroupBookmarkServiceImpl.class); + + @Override + public List getGroupsBookmarks(String user, String additionalInfo) throws DataGridConnectionRefusedException, + DataNotFoundException, JargonException { + + List groups = new ArrayList(); + + if (user == null || additionalInfo == null || user.isEmpty() || additionalInfo.isEmpty()) { + logger.error("Could not get groups bookmarks. Username or zone empty"); + return groups; + } + + logger.info("Get groups bookmarks for {}", user); + + UserAO userAO = adminServices.getUserAO(); + User iRodsUser = userAO.findByName(user); + + if (iRodsUser != null) { + String[] groupIds = userService.getGroupIdsForUser(user, additionalInfo); + groups = groupService.findByDataGridIdList(groupIds); + } + + return groups; + } + + @Override + public boolean updateBookmarks(DataGridGroup group, Set toAdd, Set toRemove) { + + boolean operationResult = true; + + try { + if (toAdd != null) { + for (String path : toAdd) { + if (!findBookmarksForGroupAsString(group).contains(path)) { + groupBookmarkDao.addByGroupAndPath(group, path, collectionService.isCollection(path)); + } + } + } + + if (toRemove != null) { + for (String path : toRemove) { + groupBookmarkDao.removeByGroupAndPath(group, path); + } + } + } + catch (Exception e) { + operationResult = false; + logger.error("Could not modify group bookmark for {}", group.getGroupname(), e); + } + + return operationResult; + } + + @Override + public List findBookmarksForGroupAsString(DataGridGroup group) { + List bookmarks = groupBookmarkDao.findByGroup(group); + List strings = new ArrayList(); + + for (DataGridGroupBookmark bookmark : bookmarks) { + strings.add(bookmark.getPath()); + } + + Collections.sort(strings); + + return strings; + } + + @Override + public List getGroupsBookmarksPaginated(String user, String additionalInfo, int offset, int limit, String searchString, + String orderBy, String orderDir, boolean onlyCollections) throws DataGridConnectionRefusedException, DataNotFoundException, + JargonException { + + List groupBookmarks = new ArrayList(); + + if (user == null || additionalInfo == null || user.isEmpty() || additionalInfo.isEmpty()) { + logger.error("Could not get groups bookmarks. Username or zone empty"); + return groupBookmarks; + } + + logger.info("Get groups bookmarks for {}", user); + + UserAO userAO = adminServices.getUserAO(); + User iRodsUser = userAO.findByName(user); + + if (iRodsUser != null) { + String[] groupIds = userService.getGroupIdsForUser(user, additionalInfo); + groupBookmarks = groupBookmarkDao + .findGroupBookmarksByGroupsIds(groupIds, offset, limit, searchString, orderBy, orderDir, onlyCollections); + } + + return groupBookmarks; + } + + @Override + public Integer countTotalGroupBookmarks(String user, String additionalInfo) throws DataGridConnectionRefusedException, DataNotFoundException, + JargonException { + Integer totalGroupBookmarks = 0; + if (user == null || additionalInfo == null || user.isEmpty() || additionalInfo.isEmpty()) { + logger.error("Could not get groups bookmarks. Username or zone empty"); + return 0; + } + + logger.info("Get groups bookmarks for {}", user); + + UserAO userAO = adminServices.getUserAO(); + User iRodsUser = userAO.findByName(user); + + if (iRodsUser != null) { + String[] groupIds = userService.getGroupIdsForUser(user, additionalInfo); + totalGroupBookmarks = groupBookmarkDao.countGroupBookmarksByGroupsIds(groupIds).intValue(); + } + return totalGroupBookmarks; + } + + @Override + public List findBookmarksOnPath(String path) { + return groupBookmarkDao.findBookmarksByPath(path); + } + + @Override + public boolean removeBookmarkBasedOnPath(String path) { + return groupBookmarkDao.removeByPath(path); + } + + @Override + public boolean removeBookmarkBasedOnRelativePath(String path) { + return groupBookmarkDao.removeByParentPath(path); + } + + @Override + public boolean removeBookmarkBasedOnGroup(DataGridGroup group) { + return groupBookmarkDao.removeByGroup(group); + } + + @Override + public boolean updateBookmark(String oldPath, String newPath) { + return groupBookmarkDao.updateBookmark(oldPath, newPath); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/GroupServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/GroupServiceImpl.java new file mode 100755 index 000000000..fc119c509 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/GroupServiceImpl.java @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.dao.GroupDao; +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.*; +import org.irods.jargon.core.exception.DuplicateDataException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.CollectionAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.UserGroupAO; +import org.irods.jargon.core.pub.domain.User; +import org.irods.jargon.core.pub.domain.UserGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +@Service +@Transactional +public class GroupServiceImpl implements GroupService { + + @Autowired + CollectionService collectionService; + + @Autowired + UserBookmarkService userBookmarkService; + + @Autowired + GroupBookmarkService groupBookmarkService; + + @Autowired + IRODSServices irodsServices; + + @Autowired + GroupDao groupDao; + + @Autowired + private ConfigService configService; + + private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); + + @Override + public List findAll() { + List groups = groupDao.findAll(DataGridGroup.class); + Collections.sort(groups); + return groups; + } + + @Override + public List findByGroupname(String groupname) { + List groups = groupDao.findByGroupname(groupname); + Collections.sort(groups); + return groups; + } + + @Override + public DataGridGroup findByGroupnameAndZone(String groupname, String zone) { + return groupDao.findByGroupnameAndZone(groupname, zone); + } + + @Override + public boolean createGroup(DataGridGroup newGroup, List usersToBeAttached) throws DataGridConnectionRefusedException { + + UserGroupAO groupAO = irodsServices.getGroupAO(); + + // Translating to iRODS model format + UserGroup irodsGroup = new UserGroup(); + + irodsGroup.setUserGroupName(newGroup.getGroupname()); + irodsGroup.setZone(newGroup.getAdditionalInfo()); + + try { + + irodsGroup.setUserGroupName(newGroup.getGroupname()); + irodsGroup.setZone(newGroup.getAdditionalInfo()); + + // creating group in iRODS + groupAO.addUserGroup(irodsGroup); + + // Recovering the recently created group to get the data grid id. + irodsGroup = groupAO.findByName(irodsGroup.getUserGroupName()); + newGroup.setDataGridId(Long.parseLong(irodsGroup.getUserGroupId())); + + // Persisting the new group into our database + groupDao.save(newGroup); + + // attaching users to this group + updateMemberList(newGroup, usersToBeAttached); + + return true; + } + catch (DuplicateDataException e) { + logger.error("UserGroup " + newGroup.getGroupname() + " already exists: ", e); + } + catch (JargonException e) { + logger.error("Could not execute createGroup() on UserGroupAO class: ", e); + } + + return false; + } + + @Override + public boolean deleteGroupByGroupname(String groupname) throws DataGridConnectionRefusedException { + boolean groupDeleted = false; + + UserGroupAO groupAO = irodsServices.getGroupAO(); + + try { + + DataGridGroup group = groupDao.findByGroupnameAndZone(groupname, configService.getIrodsZone()); + + // remove group from the data grid + groupAO.removeUserGroup(groupname); + + // Removing group bookmarks associated to this group + userBookmarkService.removeBookmarkBasedOnPath(String.format("/%s/home/%s", configService.getIrodsZone(), groupname)); + groupBookmarkService.removeBookmarkBasedOnGroup(group); + + // remove user from the Mlx database + groupDeleted = groupDao.deleteByGroupname(groupname); + } + catch (JargonException e) { + logger.error("Could not execute removeUserGroup(String groupname)/" + "deleteByGroupname(groupname) on UserGroupAO/GroupDao class(es): ", + e); + } + catch (Exception e) { + logger.error("Could not execute delete group (dao)"); + } + + return groupDeleted; + } + + @Override + public boolean attachUserToGroup(DataGridUser user, DataGridGroup group) throws DataGridConnectionRefusedException { + + UserGroupAO groupAO = irodsServices.getGroupAO(); + try { + groupAO.addUserToGroup(group.getGroupname(), user.getUsername(), group.getAdditionalInfo()); + return true; + } + catch (Exception e) { + logger.info("Could not attach user [" + user.getUsername() + "] to group [" + group.getGroupname() + "]: ", e); + } + return false; + } + + @Override + public boolean removeUserFromGroup(DataGridUser user, DataGridGroup group) throws DataGridConnectionRefusedException { + + UserGroupAO groupAO = irodsServices.getGroupAO(); + try { + groupAO.removeUserFromGroup(group.getGroupname(), user.getUsername(), group.getAdditionalInfo()); + return true; + } + catch (Exception e) { + logger.info("Could not remove user [" + user.getUsername() + "] from group [" + group.getGroupname() + "]: ", e); + } + return false; + } + + @Override + public boolean updateMemberList(DataGridGroup group, List users) throws DataGridConnectionRefusedException { + + try { + + UserGroupAO groupAO = irodsServices.getGroupAO(); + + // Users that are currently on this group + List usersFromIrods = groupAO.listUserGroupMembers(group.getGroupname()); + + // Building set with iRODS IDs already on this group + HashMap idsFromIrods = new HashMap(); + for (User userFromIrods : usersFromIrods) { + idsFromIrods.put(Long.valueOf(userFromIrods.getId()), userFromIrods); + } + + // Building set with iRODS IDs coming from UI + HashMap idsFromUi = new HashMap(); + for (DataGridUser userFromUi : users) { + idsFromUi.put(userFromUi.getDataGridId(), userFromUi); + } + + // Resolving differences from UI to iRODS + Set keysFromUi = idsFromUi.keySet(); + Set keysFromIrods = idsFromIrods.keySet(); + + for (Long dataGridId : keysFromUi) { + if (!keysFromIrods.contains(dataGridId)) { + attachUserToGroup(idsFromUi.get(dataGridId), group); + } + } + + for (Long dataGridId : keysFromIrods) { + if (!keysFromUi.contains(dataGridId)) { + DataGridUser user = new DataGridUser(); + user.setUsername(idsFromIrods.get(dataGridId).getName()); + removeUserFromGroup(user, group); + } + } + + return true; + } + catch (Exception e) { + logger.info("Could not update [" + group.getGroupname() + "]: ", e); + } + return false; + } + + @Override + public List findByQueryString(String query) { + List groups = groupDao.findByQueryString(query); + Collections.sort(groups); + return groups; + } + + @Override + public String[] getMemberList(DataGridGroup group) throws DataGridConnectionRefusedException { + UserGroupAO userGroupAO = irodsServices.getGroupAO(); + try { + List groupMembers = userGroupAO.listUserGroupMembers(group.getGroupname()); + String[] dataGridIds = new String[groupMembers.size()]; + for (int i = 0; i < groupMembers.size(); i++) { + dataGridIds[i] = groupMembers.get(i).getId(); + } + return dataGridIds; + } + catch (JargonException e) { + logger.error("Could not get members list for group [" + group.getGroupname() + "]: ", e); + } + return new String[0]; + } + + @Override + public List findByDataGridIdList(String[] ids) { + List groups = groupDao.findByDataGridIdList(ids); + Collections.sort(groups); + return groups; + } + + @Override + public List findByGroupNameList(String[] groupNames) { + List groups = groupDao.findByGroupNameList(groupNames); + Collections.sort(groups); + return groups; + } + + @Override + public List findByIdList(String[] ids) { + + if (ids != null) { + Long[] longIds = new Long[ids.length]; + for (int i = 0; i < ids.length; i++) { + longIds[i] = Long.valueOf(ids[i]); + } + List groups = groupDao.findByIdList(longIds); + Collections.sort(groups); + return groups; + } + return new ArrayList(); + } + + @Override + public boolean updateReadPermissions(DataGridGroup group, Map addCollectionsToRead, Map removeCollectionsToRead) + throws DataGridConnectionRefusedException { + + CollectionAO collectionAO = irodsServices.getCollectionAO(); + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + + try { + for (String path : addCollectionsToRead.keySet()) { + if (collectionService.isCollection(path)) { + collectionAO + .setAccessPermissionReadAsAdmin(group.getAdditionalInfo(), path, group.getGroupname(), addCollectionsToRead.get(path)); + } + else { + dataObjectAO.setAccessPermissionReadInAdminMode(group.getAdditionalInfo(), path, group.getGroupname()); + } + } + for (String path : removeCollectionsToRead.keySet()) { + if (collectionService.isCollection(path)) { + collectionAO.removeAccessPermissionForUserAsAdmin(group.getAdditionalInfo(), path, group.getGroupname(), + removeCollectionsToRead.get(path)); + } + else { + dataObjectAO.setAccessPermissionReadInAdminMode(group.getAdditionalInfo(), path, group.getGroupname()); + } + } + return true; + } + catch (JargonException | DataGridException e) { + logger.error("Could not set read permission:", e); + } + return false; + } + + @Override + public boolean updateWritePermissions(DataGridGroup group, Map addCollectionsToWrite, + Map removeCollectionsToWrite) throws DataGridConnectionRefusedException { + + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + CollectionAO collectionAO = irodsServices.getCollectionAO(); + + try { + for (String path : addCollectionsToWrite.keySet()) { + if (collectionService.isCollection(path)) { + collectionAO.setAccessPermissionWriteAsAdmin(group.getAdditionalInfo(), path, group.getGroupname(), + addCollectionsToWrite.get(path)); + } + else { + dataObjectAO.setAccessPermissionWriteInAdminMode(group.getAdditionalInfo(), path, group.getGroupname()); + } + } + for (String path : removeCollectionsToWrite.keySet()) { + if (collectionService.isCollection(path)) { + collectionAO.removeAccessPermissionForUserAsAdmin(group.getAdditionalInfo(), path, group.getGroupname(), + removeCollectionsToWrite.get(path)); + } + else { + dataObjectAO.removeAccessPermissionsForUserInAdminMode(group.getAdditionalInfo(), path, group.getGroupname()); + } + } + return true; + } + catch (JargonException | DataGridException e) { + logger.error("Could not set read permission:", e); + } + return false; + } + + @Override + public boolean updateOwnership(DataGridGroup group, Map addCollectionsToOwn, Map removeCollectionsToOwn) + throws DataGridConnectionRefusedException { + + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + CollectionAO collectionAO = irodsServices.getCollectionAO(); + try { + for (String path : addCollectionsToOwn.keySet()) { + if (collectionService.isCollection(path)) { + collectionAO.setAccessPermissionOwnAsAdmin(group.getAdditionalInfo(), path, group.getGroupname(), addCollectionsToOwn.get(path)); + } + else { + dataObjectAO.setAccessPermissionWriteInAdminMode(group.getAdditionalInfo(), path, group.getGroupname()); + } + } + for (String path : removeCollectionsToOwn.keySet()) { + if (collectionService.isCollection(path)) { + collectionAO.removeAccessPermissionForUserAsAdmin(group.getAdditionalInfo(), path, group.getGroupname(), + removeCollectionsToOwn.get(path)); + } + else { + dataObjectAO.removeAccessPermissionsForUserInAdminMode(group.getAdditionalInfo(), path, group.getGroupname()); + } + } + return true; + } + catch (JargonException | DataGridException e) { + logger.error("Could not set ownership:", e); + } + return false; + } + + @Override + public int countAll() { + return groupDao.findAll(DataGridGroup.class).size(); + } + + @Override + public String getGroupCollectionPath(String groupName) { + if (groupName == null || groupName.isEmpty()) { + return ""; + } + + return String.format("/%s/home/%s", configService.getIrodsZone(), groupName); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/IRODSServicesImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/IRODSServicesImpl.java new file mode 100755 index 000000000..ac61deb98 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/IRODSServicesImpl.java @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import java.net.ConnectException; + +import org.irods.jargon.core.connection.IRODSAccount; +import org.irods.jargon.core.connection.IrodsVersion; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.BulkFileOperationsAO; +import org.irods.jargon.core.pub.CollectionAO; +import org.irods.jargon.core.pub.CollectionAndDataObjectListAndSearchAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.DataTransferOperations; +import org.irods.jargon.core.pub.EnvironmentalInfoAO; +import org.irods.jargon.core.pub.IRODSAccessObjectFactory; +import org.irods.jargon.core.pub.IRODSFileSystemAO; +import org.irods.jargon.core.pub.RemoteExecutionOfCommandsAO; +import org.irods.jargon.core.pub.ResourceAO; +import org.irods.jargon.core.pub.RuleProcessingAO; +import org.irods.jargon.core.pub.SpecificQueryAO; +import org.irods.jargon.core.pub.Stream2StreamAO; +import org.irods.jargon.core.pub.UserAO; +import org.irods.jargon.core.pub.UserGroupAO; +import org.irods.jargon.core.pub.ZoneAO; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.irods.jargon.ticket.TicketAdminService; +import org.irods.jargon.ticket.TicketServiceFactory; +import org.irods.jargon.ticket.TicketServiceFactoryImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.auth.UserTokenDetails; +import com.emc.metalnx.services.interfaces.IRODSServices; + +@Service("irodsServices") +@Transactional +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES) +public class IRODSServicesImpl implements IRODSServices { + + @Autowired + IRODSAccessObjectFactory irodsAccessObjectFactory; + + private UserTokenDetails userTokenDetails; + private IRODSAccount irodsAccount; + + private static final Logger logger = LoggerFactory.getLogger(IRODSServicesImpl.class); + + public IRODSServicesImpl() { + /* + * This is a shim to support testing, probably a dependency on seccontextholder + * is unwarranted and should be factored out of this layer in the future - mcc + */ + try { + this.userTokenDetails = (UserTokenDetails) SecurityContextHolder.getContext().getAuthentication() + .getDetails(); + this.irodsAccount = this.userTokenDetails.getIrodsAccount(); + + } catch (NullPointerException npe) { + logger.warn("null pointer getting security context, assume running outside of container", npe); + } + } + + public IRODSServicesImpl(IRODSAccount acct) { + this.irodsAccount = acct; + } + + @Override + public TicketAdminService getTicketAdminService() throws DataGridConnectionRefusedException { + TicketAdminService tas = null; + + try { + TicketServiceFactory tsf = new TicketServiceFactoryImpl(irodsAccessObjectFactory); + tas = tsf.instanceTicketAdminService(irodsAccount); + } catch (JargonException e) { + logger.error("Could not instantiate ticket admin service: ", e.getMessage()); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return tas; + } + + @Override + public String findIRodsVersion() throws DataGridConnectionRefusedException { + String version = ""; + + try { + EnvironmentalInfoAO envInfoAO = irodsAccessObjectFactory.getEnvironmentalInfoAO(irodsAccount); + IrodsVersion iv = envInfoAO.getIRODSServerPropertiesFromIRODSServer().getIrodsVersion(); + version = String.format("%s.%s.%s", iv.getMajorAsString(), iv.getMinorAsString(), iv.getPatchAsString()); + } catch (JargonException e) { + logger.error("Could not find iRODS version: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return version; + } + + @Override + public BulkFileOperationsAO getBulkFileOperationsAO() throws DataGridConnectionRefusedException { + BulkFileOperationsAO bulkFileOperationsAO = null; + + try { + // Returning UserAO instance + bulkFileOperationsAO = irodsAccessObjectFactory.getBulkFileOperationsAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate UserAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return bulkFileOperationsAO; + } + + @Override + public String getCurrentUser() { + return irodsAccount.getUserName(); + } + + @Override + public String getCurrentUserZone() { + return irodsAccount.getZone(); + } + + @Override + public UserAO getUserAO() throws DataGridConnectionRefusedException { + try { + // Returning UserAO instance + return irodsAccessObjectFactory.getUserAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate UserAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public UserGroupAO getGroupAO() throws DataGridConnectionRefusedException { + try { + + // Returning UserAO instance + return irodsAccessObjectFactory.getUserGroupAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate UserAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public CollectionAO getCollectionAO() throws DataGridConnectionRefusedException { + try { + + // Returning CollectionAO instance + return irodsAccessObjectFactory.getCollectionAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate CollectionAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public CollectionAndDataObjectListAndSearchAO getCollectionAndDataObjectListAndSearchAO() + throws DataGridConnectionRefusedException { + + try { + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getCollectionAndDataObjectListAndSearchAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate CollectionAndDataObjectListAndSearchAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return null; + } + + @Override + public IRODSFileSystemAO getIRODSFileSystemAO() throws DataGridConnectionRefusedException { + + try { + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getIRODSFileSystemAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate CollectionAndDataObjectListAndSearchAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public IRODSFileFactory getIRODSFileFactory() throws DataGridConnectionRefusedException { + try { + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getIRODSFileFactory(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate IRODSFileFactory: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public DataTransferOperations getDataTransferOperations() throws DataGridConnectionRefusedException { + try { + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getDataTransferOperations(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate DataTransferOperations: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return null; + } + + @Override + public Stream2StreamAO getStream2StreamAO() throws DataGridConnectionRefusedException { + + try { + + return irodsAccessObjectFactory.getStream2StreamAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate Stream2StreamAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return null; + } + + @Override + public SpecificQueryAO getSpecificQueryAO() throws DataGridConnectionRefusedException { + try { + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getSpecificQueryAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate CollectionAndDataObjectListAndSearchAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public RemoteExecutionOfCommandsAO getRemoteExecutionOfCommandsAO() throws DataGridConnectionRefusedException { + try { + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getRemoteExecutionOfCommandsAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate RemoteExecutionOfCommandsAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public ResourceAO getResourceAO() throws DataGridConnectionRefusedException { + try { + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getResourceAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate CollectionAndDataObjectListAndSearchAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public ZoneAO getZoneAO() throws DataGridConnectionRefusedException { + try { + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getZoneAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate CollectionAndDataObjectListAndSearchAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return null; + } + + @Override + public DataObjectAO getDataObjectAO() throws DataGridConnectionRefusedException { + try { + + // Returning CollectionAndDataObjectListAndSearchAO instance + return irodsAccessObjectFactory.getDataObjectAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate CollectionAndDataObjectListAndSearchAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + + return null; + } + + @Override + public RuleProcessingAO getRuleProcessingAO() throws DataGridConnectionRefusedException { + try { + // Returning RuleProcessingAO instance + return irodsAccessObjectFactory.getRuleProcessingAO(irodsAccount); + + } catch (JargonException e) { + logger.error("Could not instantiate RuleProcessingAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return null; + } + + @Override + public void setDefaultStorageResource(String newResourceName) { + this.irodsAccount.setDefaultStorageResource(newResourceName); + } + + @Override + public String getDefaultStorageResource() { + return this.irodsAccount.getDefaultStorageResource(); + } + + @Override + public EnvironmentalInfoAO getEnvironmentalInfoAO() throws DataGridConnectionRefusedException { + EnvironmentalInfoAO env = null; + + try { + env = irodsAccessObjectFactory.getEnvironmentalInfoAO(this.irodsAccount); + } catch (JargonException e) { + logger.error("Could not instantiate EnvironmentalInfoAO: ", e); + + if (e.getCause() instanceof ConnectException) { + throw new DataGridConnectionRefusedException(e.getMessage()); + } + } + return env; + } + + @Override + public boolean isAtLeastIrods420() throws DataGridConnectionRefusedException { + boolean isAtLeastIrods420 = false; + + try { + EnvironmentalInfoAO env = irodsAccessObjectFactory.getEnvironmentalInfoAO(this.irodsAccount); + if (env != null) + isAtLeastIrods420 = env.getIRODSServerPropertiesFromIRODSServer().isAtLeastIrods420(); + } catch (JargonException e) { + logger.error("Could not get environmental information from grid: {}", e.getMessage()); + } + + return isAtLeastIrods420; + } + + @Override + public IRODSAccessObjectFactory getIrodsAccessObjectFactory() { + return irodsAccessObjectFactory; + } + + public UserTokenDetails getUserTokenDetails() { + return userTokenDetails; + } + + public void setUserTokenDetails(UserTokenDetails userTokenDetails) { + this.userTokenDetails = userTokenDetails; + } + + public IRODSAccount getIrodsAccount() { + return irodsAccount; + } + + public void setIrodsAccount(IRODSAccount irodsAccount) { + this.irodsAccount = irodsAccount; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/MSIServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/MSIServiceImpl.java new file mode 100755 index 000000000..7839edc0b --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/MSIServiceImpl.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import com.emc.metalnx.core.domain.entity.DataGridMSIPkgInfo; +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; +import com.emc.metalnx.core.domain.utils.DataGridCoreUtils; +import com.emc.metalnx.services.interfaces.ConfigService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.MSIService; +import com.emc.metalnx.services.interfaces.ResourceService; +import com.emc.metalnx.services.interfaces.RuleService; + +@Service +@Transactional +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES) +public class MSIServiceImpl implements MSIService { + private static final Logger logger = LoggerFactory.getLogger(MSIServiceImpl.class); + + @Autowired + private RuleService ruleService; + + @Autowired + private ResourceService resourceService; + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private ConfigService configService; + + private List servers = new ArrayList<>(); + + @Override + public DataGridMSIPkgInfo getMSIPkgInfo() throws DataGridConnectionRefusedException { + return new DataGridMSIPkgInfo(getMSIInfoForAllServers(), configService.getMsiAPIVersionSupported()); + } + + @Override + public DataGridServer getMSIsInstalled(String host) throws DataGridConnectionRefusedException { + if (host == null || host.isEmpty()) + return null; + return findServerByHostname(host); + } + + @Override + public List getMSIInfoForAllServers() throws DataGridConnectionRefusedException { + servers = resourceService.getAllResourceServers(resourceService.findAll()); + for (DataGridServer server : servers) + setMSIInfoForServer(server); + return servers; + } + + @Override + public void setMSIInfoForServer(DataGridServer server) throws DataGridConnectionRefusedException { + List irodsMSIs = irodsServices.isAtLeastIrods420() ? configService.getIrods42MSIsExpected() + : configService.getIrods41MSIsExpected(); + + server.setMetalnxExpectedMSIs(configService.getMlxMSIsExpected()); + server.setIRodsExpectedMSIs(irodsMSIs); + server.setOtherExpectedMSIs(configService.getOtherMSIsExpected()); + + try { + server.setMSIVersion(ruleService.execGetVersionRule(server.getHostname())); + } catch (DataGridRuleException e) { + logger.error("Failed to get MSI version for server: ", server.getHostname()); + } + + try { + server.setMSIInstalledList(ruleService.execGetMSIsRule(server.getHostname())); + } catch (DataGridRuleException e) { + logger.error("Failed to get MSIs installed for server: ", server.getHostname()); + } + + } + + @Override + public boolean isMSIAPICompatibleInResc(String resource) throws DataGridConnectionRefusedException { + if (servers == null || servers.isEmpty()) + getMSIInfoForAllServers(); + + DataGridServer server = null; + + for (DataGridServer s : servers) { + for (DataGridResource dgResc : s.getResources()) { + if (resource.equals(dgResc.getName())) { + server = s; + break; + } + } + + if (server != null) + break; + } + + String apiVersionSupported = DataGridCoreUtils.getAPIVersion(configService.getMsiAPIVersionSupported()); + String apiVersionInstalled = server != null ? DataGridCoreUtils.getAPIVersion(server.getMSIVersion()) : ""; + return apiVersionSupported.equalsIgnoreCase(apiVersionInstalled); + } + + /** + * Looks for a server instance based on its hostname + * + * @param host + * server's hostname + * @return server instance + */ + private DataGridServer findServerByHostname(String host) throws DataGridConnectionRefusedException { + getMSIInfoForAllServers(); // update list of servers + + DataGridServer server = null; + + for (DataGridServer s : servers) { + if (host.equals(s.getHostname())) { + server = s; + break; + } + } + + return server; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/MetadataServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/MetadataServiceImpl.java new file mode 100755 index 000000000..d7c896a26 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/MetadataServiceImpl.java @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.protovalues.FilePermissionEnum; +import org.irods.jargon.core.pub.CollectionAO; +import org.irods.jargon.core.pub.CollectionAndDataObjectListAndSearchAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.IRODSFileSystemAO; +import org.irods.jargon.core.pub.domain.AvuData; +import org.irods.jargon.core.pub.domain.DataObject; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.irods.jargon.core.query.JargonQueryException; +import org.irods.jargon.core.query.MetaDataAndDomainData; +import org.irods.jargon.core.query.SpecificQueryResultSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.entity.DataGridMetadataSearch; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.PermissionsService; +import com.emc.metalnx.services.interfaces.SpecQueryService; +import com.emc.metalnx.services.interfaces.UserService; +import com.emc.metalnx.services.machine.util.DataGridUtils; + +@Service +@Transactional +public class MetadataServiceImpl implements MetadataService { + @Autowired + IRODSServices irodsServices; + + @Autowired + SpecQueryService specQueryService; + + @Autowired + UserService userService; + + @Autowired + PermissionsService permissionsService; + + private static final Logger logger = LoggerFactory.getLogger(MetadataServiceImpl.class); + + @Override + public List findByMetadata(List searchList, + DataGridPageContext pageContext, int pageNum, int pageSize) throws DataGridConnectionRefusedException { + + List dataGridCollectionAndDataObjects = new ArrayList<>(); + List dataGridObjects; + List dataGridCollections; + + int totalCollections; + int totalDataObjects; + int startIndex = (pageNum - 1) * pageSize; + int endIndex = (pageNum * pageSize) - 1; + int endIndexForDataObjs; + int endIndexForCollections; + + try { + String zone = irodsServices.getCurrentUserZone(); + + totalCollections = specQueryService.countCollectionsMatchingMetadata(searchList, zone); + totalDataObjects = specQueryService.countDataObjectsMatchingMetadata(searchList, zone); + + pageContext.setStartItemNumber(startIndex + 1); + pageContext.setTotalNumberOfItems(totalCollections + totalDataObjects); + + if (endIndex + 1 <= totalCollections) { + // looking for collections + SpecificQueryResultSet resultSetColls = specQueryService.searchByMetadata(searchList, zone, true, + pageContext, startIndex, pageSize); + + dataGridCollections = DataGridUtils.mapMetadataResultSetToDataGridCollections(resultSetColls); + + endIndexForCollections = dataGridCollections.size(); + + dataGridCollectionAndDataObjects.addAll(dataGridCollections); + + pageContext.setEndItemNumber(pageContext.getStartItemNumber() + endIndexForCollections - 1); + } else if (startIndex + 1 > totalCollections) { + // looking for data objects + SpecificQueryResultSet resultSetDataObjs = specQueryService.searchByMetadata(searchList, zone, false, + pageContext, startIndex - totalCollections, pageSize); + + dataGridObjects = DataGridUtils.mapMetadataResultSetToDataGridObjects(resultSetDataObjs); + + pageContext.setEndItemNumber(pageContext.getStartItemNumber() + dataGridObjects.size() - 1); + + dataGridCollectionAndDataObjects.addAll(dataGridObjects); + } else { + // looking for collections + SpecificQueryResultSet resultSetColls = specQueryService.searchByMetadata(searchList, zone, true, + pageContext, startIndex, pageSize); + + dataGridCollections = DataGridUtils.mapMetadataResultSetToDataGridCollections(resultSetColls); + + endIndexForDataObjs = pageSize - (totalCollections % pageSize); + + // looking for data objects + SpecificQueryResultSet resultSetDataObjs = specQueryService.searchByMetadata(searchList, zone, false, + pageContext, 0, endIndexForDataObjs); + + dataGridObjects = DataGridUtils.mapMetadataResultSetToDataGridObjects(resultSetDataObjs); + + endIndexForDataObjs = endIndexForDataObjs > dataGridObjects.size() ? dataGridObjects.size() + : endIndexForDataObjs; + + dataGridCollectionAndDataObjects.addAll(dataGridCollections); + dataGridCollectionAndDataObjects.addAll(dataGridObjects); + + pageContext.setEndItemNumber( + pageContext.getStartItemNumber() + endIndexForDataObjs + dataGridCollections.size() - 1); + } + + } catch (DataGridConnectionRefusedException e) { + throw e; + } catch (Exception e) { + logger.error("Could not find data objects by metadata. ", e.getMessage()); + } + + populateVisibilityForCurrentUser(dataGridCollectionAndDataObjects); + return dataGridCollectionAndDataObjects; + } + + @Override + public List findMetadataValuesByPath(String path) throws DataGridConnectionRefusedException { + + List metadataList; + List dataGridMetadataList = new ArrayList<>(); + List resultingList; + + CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsServices + .getCollectionAndDataObjectListAndSearchAO(); + + try { + Object obj = collectionAndDataObjectListAndSearchAO.getFullObjectForType(path); + + if (obj instanceof DataObject) { + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + metadataList = dataObjectAO.findMetadataValuesForDataObject(path); + } else { + CollectionAO collectionAO = irodsServices.getCollectionAO(); + metadataList = collectionAO.findMetadataValuesForCollection(path); + } + + // TODO2: Making sure all AVUs are unique. Jargon should do that. + resultingList = new ArrayList<>(); + Set setOfAlreadyListedAVUs = new HashSet<>(); + for (MetaDataAndDomainData avuForItem : metadataList) { + + int avuId = avuForItem.getAvuId(); + + if (!setOfAlreadyListedAVUs.contains(avuId)) { + resultingList.add(avuForItem); + setOfAlreadyListedAVUs.add(avuId); + } + } + + for (MetaDataAndDomainData metadata : resultingList) { + DataGridMetadata dataGridMetadata = new DataGridMetadata(); + dataGridMetadata.setAttribute(metadata.getAvuAttribute()); + dataGridMetadata.setValue(metadata.getAvuValue()); + dataGridMetadata.setUnit(metadata.getAvuUnit()); + dataGridMetadataList.add(dataGridMetadata); + } + + Collections.sort(dataGridMetadataList); + } catch (JargonQueryException e) { + logger.error("Error getting metadata info from collection: " + e.toString()); + } catch (JargonException e) { + logger.error("Error getting metadata info from dataobject: " + e.toString()); + } + + return dataGridMetadataList; + } + + @Override + public boolean addMetadataToPath(String path, String attribute, String value, String unit) + throws DataGridConnectionRefusedException { + + CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsServices + .getCollectionAndDataObjectListAndSearchAO(); + boolean isMetadataAdded = false; + + logger.debug(path + ": " + attribute + " " + value + " " + unit); + + try { + AvuData avuData = new AvuData(attribute, value, unit); + Object obj = collectionAndDataObjectListAndSearchAO.getFullObjectForType(path); + + if (obj instanceof DataObject) { + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + dataObjectAO.addAVUMetadata(path, avuData); + } else { + CollectionAO collectionAO = irodsServices.getCollectionAO(); + collectionAO.addAVUMetadata(path, avuData); + } + + isMetadataAdded = true; + } catch (JargonException e) { + logger.error("Error trying to add metadata: " + e); + } + return isMetadataAdded; + } + + @Override + public boolean addMetadataToPath(String path, DataGridMetadata metadata) throws DataGridConnectionRefusedException { + if (metadata == null) + return false; + return addMetadataToPath(path, metadata.getAttribute(), metadata.getValue(), metadata.getUnit()); + } + + @Override + public boolean addMetadataToPath(String path, List metadataList) + throws DataGridConnectionRefusedException { + if (metadataList == null || metadataList.isEmpty()) + return false; + + boolean isMetadataAdded = false; + + for (DataGridMetadata metadata : metadataList) { + isMetadataAdded &= addMetadataToPath(path, metadata); + } + + return isMetadataAdded; + } + + @Override + public boolean modMetadataFromPath(String path, String oldAttribute, String oldValue, String oldUnit, + String newAttribute, String newValue, String newUnit) throws DataGridConnectionRefusedException { + + CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsServices + .getCollectionAndDataObjectListAndSearchAO(); + + try { + AvuData oldAVUData = new AvuData(oldAttribute, oldValue, oldUnit); + AvuData newAVUData = new AvuData(newAttribute, newValue, newUnit); + Object obj = collectionAndDataObjectListAndSearchAO.getFullObjectForType(path); + + if (obj instanceof DataObject) { + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + dataObjectAO.modifyAVUMetadata(path, oldAVUData, newAVUData); + } else { + CollectionAO collectionAO = irodsServices.getCollectionAO(); + collectionAO.modifyAVUMetadata(path, oldAVUData, newAVUData); + } + } catch (JargonException e) { + logger.error("Error trying to modify metadata: " + e.toString()); + return false; + } + return true; + } + + @Override + public boolean delMetadataFromPath(String path, String attribute, String value, String unit) + throws DataGridConnectionRefusedException { + + CollectionAndDataObjectListAndSearchAO collectionAndDataObjectListAndSearchAO = irodsServices + .getCollectionAndDataObjectListAndSearchAO(); + try { + AvuData avuData = new AvuData(attribute, value, unit); + Object obj = collectionAndDataObjectListAndSearchAO.getFullObjectForType(path); + + if (obj instanceof DataObject) { + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + dataObjectAO.deleteAVUMetadata(path, avuData); + } else { + CollectionAO collectionAO = irodsServices.getCollectionAO(); + collectionAO.deleteAVUMetadata(path, avuData); + } + } catch (JargonException e) { + logger.error("Error trying to delete metadata: " + e.toString()); + return false; + } + return true; + } + + /** + * Sets whether or not a user can check an object resulting from a metadata + * search + * + * @param objectList + * list of data objects/collections + * @throws DataGridConnectionRefusedException + */ + @Override + public void populateVisibilityForCurrentUser(List objectList) + throws DataGridConnectionRefusedException { + + if (objectList == null || objectList.isEmpty()) { + return; + } + + final String currentUser = irodsServices.getCurrentUser(); + final IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + final IRODSFileSystemAO irodsFileSystemAO = irodsServices.getIRODSFileSystemAO(); + + for (final DataGridCollectionAndDataObject obj : objectList) { + try { + int resultingPermission; + final IRODSFile fileObj = irodsFileFactory.instanceIRODSFile(obj.getPath()); + + if (obj.isCollection()) { + resultingPermission = irodsFileSystemAO.getDirectoryPermissionsForGivenUser(fileObj, currentUser); + } else { + resultingPermission = irodsFileSystemAO.getFilePermissionsForGivenUser(fileObj, currentUser); + } + + // By default, the visibility of a user over an object is set to false + obj.setVisibleToCurrentUser(resultingPermission != FilePermissionEnum.NONE.getPermissionNumericValue()); + + } catch (final Exception e) { + logger.error("Could not get permissions for current user: {}", e.getMessage()); + } + } + } + + @Override + public boolean copyMetadata(String srcPath, String dstPath) throws DataGridConnectionRefusedException { + if (srcPath == null || srcPath.isEmpty() || dstPath == null || dstPath.isEmpty()) + return false; + + logger.info("Copying metadata from {} to {}", srcPath, dstPath); + + boolean isMetadataCopied = true; + + for (DataGridMetadata metadata : findMetadataValuesByPath(srcPath)) { + isMetadataCopied &= addMetadataToPath(dstPath, metadata); + } + + return isMetadataCopied; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/PermissionsServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/PermissionsServiceImpl.java new file mode 100755 index 000000000..e473dce13 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/PermissionsServiceImpl.java @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.dao.GroupBookmarkDao; +import com.emc.metalnx.core.domain.dao.GroupDao; +import com.emc.metalnx.core.domain.entity.*; +import com.emc.metalnx.core.domain.entity.enums.DataGridPermType; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.PermissionsService; +import org.irods.jargon.core.exception.FileNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.protovalues.FilePermissionEnum; +import org.irods.jargon.core.pub.CollectionAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.IRODSFileSystemAO; +import org.irods.jargon.core.pub.domain.Collection; +import org.irods.jargon.core.pub.domain.UserFilePermission; +import org.irods.jargon.core.pub.domain.UserGroup; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +@Service +@Transactional +public class PermissionsServiceImpl implements PermissionsService { + + /* + * PERMISSIONS in iRODS + * READ: Download, Copy, and Replicate + * WRITE: Download, Copy, Replicate, Metadata (templates) + * OWN: Download, Copy, Replicate, Metadata (templates), Move, Edit, and Delete + */ + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private GroupBookmarkDao groupBookmarkDao; + + @Autowired + private GroupDao groupDao; + + @Autowired + private CollectionService collectionService; + + // String representing the rodsgroup type on the UserFilePermission enum + private static final String RODS_GROUP = "rodsgroup"; + + private static final Logger logger = LoggerFactory.getLogger(PermissionsServiceImpl.class); + + @Override + public DataGridPermType findMostRestrictivePermission(String... paths) throws DataGridConnectionRefusedException { + logger.info("Find most restrictive permission"); + + DataGridPermType mostRestrictivePermission = DataGridPermType.NONE; + Set permissions = new HashSet<>(); + + for (String path : paths) { + logger.info("Get permission for {}", path); + permissions.add(collectionService.getPermissionsForPath(path)); + } + + if (permissions.contains("none")) { + mostRestrictivePermission = DataGridPermType.NONE; + } + else if (permissions.contains("read")) { + mostRestrictivePermission = DataGridPermType.READ; + } + else if (permissions.contains("write")) { + mostRestrictivePermission = DataGridPermType.WRITE; + } + else if(permissions.contains("own")){ + mostRestrictivePermission = DataGridPermType.OWN; + } + + logger.info("Most restrictive permission: {}", mostRestrictivePermission); + + return mostRestrictivePermission; + } + + @Override + public List getPathPermissionDetails(String path, String username) throws JargonException, + DataGridConnectionRefusedException { + + logger.debug("Getting permissions details for object {}", path); + List filePermissionList = this.getFilePermissionListForObject(path, username); + return mapListToListDataGridFilePermission(filePermissionList); + } + + @Override + public List getPathPermissionDetails(String path) throws JargonException, + DataGridConnectionRefusedException { + + logger.info("Getting permissions details for object {}", path); + List filePermissionList = this.getFilePermissionListForObject(path); + return mapListToListDataGridFilePermission(filePermissionList); + } + + @Override + public boolean canLoggedUserModifyPermissionOnPath(String path) throws DataGridConnectionRefusedException { + + String userName = irodsServices.getCurrentUser(); + final IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + final IRODSFileSystemAO irodsFileSystemAO = irodsServices.getIRODSFileSystemAO(); + + try { + int resultingPermission; + final IRODSFile fileObj = irodsFileFactory.instanceIRODSFile(path); + + if (irodsFileSystemAO.isDirectory(fileObj)) { + resultingPermission = irodsFileSystemAO.getDirectoryPermissionsForGivenUser(fileObj, userName); + } else { + resultingPermission = irodsFileSystemAO.getFilePermissionsForGivenUser(fileObj, userName); + } + + return resultingPermission > FilePermissionEnum.WRITE.getPermissionNumericValue(); + + } catch (final Exception e) { + logger.error("Could not get permissions for current user: {}", e.getMessage()); + } + + return false; + } + + /***********************************************************************************/ + /************************* PERMISSIONS/GROUPS PROCESSING ***************************/ + /***********************************************************************************/ + + @Override + public List getGroupsWithPermissions(List ufps) { + + // Maps from data grid ID to DataGridGroupPermission object for retrieving the group names + HashMap idGroupsPermissions = new HashMap(); + + for (DataGridFilePermission ufp : ufps) { + + // Getting only the groups, ignoring users + if (ufp.getUserType().compareTo(RODS_GROUP) == 0) { + + String groupDataGridId = ufp.getUserId(); + + // If the ID is not known yet, we need to create a new entry for it + if (!idGroupsPermissions.containsKey(groupDataGridId)) { + DataGridGroupPermission dggp = new DataGridGroupPermission(); + dggp.setDataGridId(Integer.parseInt(groupDataGridId)); + dggp.setPermission(ufp.getPermission()); + + // Creating new entry for group + idGroupsPermissions.put(groupDataGridId, dggp); + } + } + } + + // Making sure we have groups to query for + if (idGroupsPermissions.size() > 0) { + + // Getting list of unique IDs on an array + String[] groupIds = idGroupsPermissions.keySet().toArray(new String[idGroupsPermissions.size()]); + + // One single DB query to get group names + List groupObjects = groupDao.findByDataGridIdList(groupIds); + + // Setting group names to the elements on the hash map + for (DataGridGroup group : groupObjects) { + idGroupsPermissions.get(String.valueOf(group.getDataGridId())).setGroupName(group.getGroupname()); + } + } + + // Casting hash map values list to array list and returning + return new ArrayList(idGroupsPermissions.values()); + } + + /***********************************************************************************/ + /************************* PERMISSIONS/USERS PROCESSING ****************************/ + /***********************************************************************************/ + + @Override + public List getUsersWithPermissions(List ufps) { + + // List containing all the users with some kind of permissions + List usersWithPermissions = new ArrayList(); + + for (DataGridFilePermission ufp : ufps) { + + // Getting only the users, ignoring groups + if (ufp.getUserType().compareTo(RODS_GROUP) != 0) { + DataGridUserPermission dgup = new DataGridUserPermission(); + dgup.setDataGridId(Integer.parseInt(ufp.getUserId())); + dgup.setUserName(ufp.getUsername()); + dgup.setUserSystemRole(ufp.getUserType()); + dgup.setPermission(ufp.getPermission()); + usersWithPermissions.add(dgup); + } + } + + return usersWithPermissions; + } + + @Override + public boolean setPermissionOnPath(DataGridPermType permType, String uName, boolean recursive, + boolean inAdminMode, String... paths) throws DataGridConnectionRefusedException { + + logger.info("Setting {} permission on path {} for user/group {}", permType, paths, uName); + + boolean operationResult = true; + + for(String path: paths) { + try { + IRODSFile irodsFile = irodsServices.getIRODSFileFactory().instanceIRODSFile(path); + + if (irodsFile.isDirectory()) { + operationResult = chmodCollection(permType, path, recursive, uName, inAdminMode); + } else { + operationResult = chmodDataObject(permType, path, uName, inAdminMode); + } + + // If the permissions is NONE, remove all bookmarks associated to the group and the path + if (permType.equals(DataGridPermType.NONE)) { + DataGridGroup group = groupDao.findByGroupnameAndZone(uName, irodsServices.getCurrentUserZone()); + if (group != null) groupBookmarkDao.removeByGroupAndPath(group, path); + } + + logger.info("Permission {} for user {} on path {} set successfully", permType, uName, paths); + } catch (JargonException e) { + logger.error("Could not set {} permission on path {} for user/group {}", permType, path, uName, e); + operationResult = false; + } + } + + return operationResult; + } + + private boolean chmodCollection(DataGridPermType permType, String path, boolean recursive, String uName, boolean inAdminMode) throws DataGridConnectionRefusedException { + String currentZone = irodsServices.getCurrentUserZone(); + CollectionAO collectionAO = irodsServices.getCollectionAO(); + boolean isPermissionSet = false; + + try { + logger.debug("Setting {} permission on collection {} for user/group as ADMIN{}", permType, path, uName); + + if(!inAdminMode) { + FilePermissionEnum filePermission = FilePermissionEnum.valueOf(permType.toString()); + collectionAO.setAccessPermission(currentZone, path, uName, recursive, filePermission); + } + else if(permType.equals(DataGridPermType.READ)) + collectionAO.setAccessPermissionReadAsAdmin(currentZone, path, uName, recursive); + + else if(permType.equals(DataGridPermType.WRITE)) + collectionAO.setAccessPermissionWriteAsAdmin(currentZone, path, uName, recursive); + + else if(permType.equals(DataGridPermType.OWN)) + collectionAO.setAccessPermissionOwnAsAdmin(currentZone, path, uName, recursive); + + else collectionAO.removeAccessPermissionForUserAsAdmin(currentZone, path, uName, recursive); + + isPermissionSet = true; + } catch (JargonException e) { + logger.error("Could not set {} permission on path {} for user/group {}", permType, path, uName, e); + } + + return isPermissionSet; + } + + private boolean chmodDataObject(DataGridPermType permType, String path, String uName, boolean inAdminMode) throws DataGridConnectionRefusedException { + String currentZone = irodsServices.getCurrentUserZone(); + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + + logger.debug("Setting {} permission on data object {} for user/group {}", permType, path, uName); + + boolean isPermissionSet = false; + + try { + if(!inAdminMode) { + FilePermissionEnum filePermission = FilePermissionEnum.valueOf(permType.toString()); + dataObjectAO.setAccessPermission(currentZone, path, uName, filePermission); + } + else if(permType.equals(DataGridPermType.READ)) + dataObjectAO.setAccessPermissionReadInAdminMode(currentZone, path, uName); + + else if(permType.equals(DataGridPermType.WRITE)) + dataObjectAO.setAccessPermissionWriteInAdminMode(currentZone, path, uName); + + else if(permType.equals(DataGridPermType.OWN)) + dataObjectAO.setAccessPermissionOwnInAdminMode(currentZone, path, uName); + + else dataObjectAO.removeAccessPermissionsForUserInAdminMode(currentZone, path, uName); + + isPermissionSet = true; + } catch (JargonException e) { + logger.error("Could not set {} permission on path {} for user/group {}", permType, path, uName, e); + } + + return isPermissionSet; + } + + @Override + public void resolveMostPermissiveAccessForUser(DataGridCollectionAndDataObject obj, DataGridUser user) throws + DataGridException { + + if(obj == null || user == null) return; + + List userGroups; + List acl; + + try { + userGroups = irodsServices.getGroupAO().findUserGroupsForUser(user.getUsername()); + acl = getFilePermissionListForObject(obj.getPath()); + } catch (JargonException e) { + throw new DataGridException(); + } + + // Building set containing group names for current user + Set userGroupsSet = new HashSet<>(); + for (UserGroup g : userGroups) { + userGroupsSet.add(g.getUserGroupName()); + } + + // Instantiating comparison matrix for permissions + List permissions = new ArrayList<>(); + permissions.add("NONE"); + permissions.add("READ"); + permissions.add("WRITE"); + permissions.add("OWN"); + + String resultingPermission = "NONE"; + for (UserFilePermission perm : acl) { + + String permUserName = perm.getUserName(); + + // Checking if current permission is related to logged user + if (permUserName.compareTo(user.getUsername()) == 0 || userGroupsSet.contains(permUserName)) { + String permissionName = perm.getFilePermissionEnum().name(); + int userOrGroupPerm = permissions.indexOf(permissionName); + int currentPermission = permissions.indexOf(resultingPermission); + + if (userOrGroupPerm > currentPermission) { + resultingPermission = permissionName; + } + } + + if (resultingPermission.compareToIgnoreCase("OWN") == 0) { + break; + } + } + + obj.setMostPermissiveAccessForCurrentUser(resultingPermission.toLowerCase()); + } + + /***********************************************************************************/ + /******************************** PRIVATE METHODS **********************************/ + /***********************************************************************************/ + + /** + * Gets the list of file permissions on the requested object. The object can be a collection + * as a single data object. + * + * @param path the path to the object + * @return list of {@link UserFilePermission} + * @throws FileNotFoundException + * @throws JargonException + * @throws DataGridConnectionRefusedException + */ + private List getFilePermissionListForObject(String path) throws JargonException, + DataGridConnectionRefusedException { + + return this.getFilePermissionListForObject(path, ""); + } + + /** + * Gets the list of file permissions on the requested object for a particular user. The object + * can be a collection as a single data object. + * + * @param path the path to the object + * @param username user name to get the permissions on the given path. If no user name is required, + * an empty String or null should be provided + * @return list of {@link UserFilePermission} + * @throws FileNotFoundException + * @throws JargonException + * @throws DataGridConnectionRefusedException + */ + private List getFilePermissionListForObject(String path, String username) throws DataGridConnectionRefusedException, + JargonException { + Object obj = irodsServices.getCollectionAndDataObjectListAndSearchAO().getFullObjectForType(path); + + List filePermissionList = new ArrayList(); + List dataGridfilePermissionList = null; + + // If the object is a collection + if (obj instanceof Collection) { + logger.debug("Getting permission info for collection {}", path); + dataGridfilePermissionList = irodsServices.getCollectionAO().listPermissionsForCollection(path); + } + + // If the object is a data object + else { + logger.debug("Getting permission info for data object {}", path); + dataGridfilePermissionList = irodsServices.getDataObjectAO().listPermissionsForDataObject(path); + } + + // adding to the final list of permissions only permissions related to the user given + // as the parameter + if (username != null && !username.isEmpty()) { + for (UserFilePermission userFilePermission : dataGridfilePermissionList) { + if (userFilePermission.getUserName().equalsIgnoreCase(username)) { + filePermissionList.add(userFilePermission); + } + } + } else { + filePermissionList = dataGridfilePermissionList; + } + + return filePermissionList; + } + + /** + * Maps an UserFilePermission instance to DataGridFilePermission object + * + * @param ufp + * @return instance of {@link DataGridFilePermission} + */ + private DataGridFilePermission mapToDataGridFilePermission(UserFilePermission ufp) { + + logger.debug("\tMapping permissions for permissions controller"); + + DataGridFilePermission dgfp = new DataGridFilePermission(); + dgfp.setUserId(ufp.getUserId()); + dgfp.setUsername(ufp.getUserName()); + dgfp.setPermission(ufp.getFilePermissionEnum().toString()); + dgfp.setUserType(ufp.getUserType().getTextValue()); + dgfp.setUserZone(ufp.getUserZone()); + return dgfp; + } + + /** + * Maps a list of UserFilePermission instances to a list of DataGridFilePermission + * objects. + * + * @param filePermissionList + * @return list of instances of {@link DataGridFilePermission} + */ + private List mapListToListDataGridFilePermission(List filePermissionList) { + + logger.debug("Mapping list of UserFilePermissions to List of DataGridFilePermission"); + + List dgFilePermissionList = new ArrayList(); + for (UserFilePermission ufp : filePermissionList) { + DataGridFilePermission dgfp = mapToDataGridFilePermission(ufp); + dgFilePermissionList.add(dgfp); + } + return dgFilePermissionList; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/RemoteExecutionServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/RemoteExecutionServiceImpl.java new file mode 100755 index 000000000..8a797414d --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/RemoteExecutionServiceImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.RemoteExecutionService; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.RemoteExecutionOfCommandsAO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.InputStream; + +@Service +public class RemoteExecutionServiceImpl implements RemoteExecutionService { + + @Autowired + IRODSServices irodsServices; + + private static final int bufferSize = 8192; + private static final String defaultEncoding = "UTF-8"; + + //private static final Logger logger = LoggerFactory.getLogger(RemoteExecutionServiceImpl.class); + + @Override + public String execute(String command) throws JargonException, IOException, + DataGridConnectionRefusedException { + + + byte[] buffer = new byte[bufferSize]; + RemoteExecutionOfCommandsAO commandsAO = irodsServices.getRemoteExecutionOfCommandsAO(); + InputStream stream = commandsAO.executeARemoteCommandAndGetStreamGivingCommandNameAndArgs(command, ""); + + StringBuilder resultBuilder = new StringBuilder(); + + while(stream.read(buffer) != -1) { + resultBuilder.append(new String(buffer, defaultEncoding)); + } + + stream.close(); + + return resultBuilder.toString(); + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/ResourceServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/ResourceServiceImpl.java new file mode 100755 index 000000000..feed7f03e --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/ResourceServiceImpl.java @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridResourceType; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.entity.enums.DataGridResourceTypeEnum; +import com.emc.metalnx.core.domain.entity.enums.DataGridServerType; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.MachineInfoService; +import com.emc.metalnx.services.interfaces.ResourceService; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.ResourceAO; +import org.irods.jargon.core.pub.ZoneAO; +import org.irods.jargon.core.pub.domain.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.net.UnknownHostException; +import java.util.*; + +@Service("resourceService") +@Transactional +public class ResourceServiceImpl implements ResourceService { + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private MachineInfoService machineInfoService; + + private static final Logger logger = LoggerFactory.getLogger(ResourceServiceImpl.class); + + @Override + public List findAll() throws DataGridConnectionRefusedException { + logger.info("Find all resources in the grid"); + + List dataGridResources = new ArrayList(); + ResourceAO resourceAO = irodsServices.getResourceAO(); + + try { + List resources = resourceAO.findAll(); + + for (Resource irodsResource : resources) { + DataGridResource newDataGridResource = getDataGridResource(irodsResource); + dataGridResources.add(newDataGridResource); + for (Resource r : resources) { + if (r.getParentName().equals(irodsResource.getName())) { + newDataGridResource.addChildResc(r.getName()); + } + } + } + } + catch (JargonException e) { + logger.error("Could not find all resources: ", e); + } + + // sorting this list alphabetically + Collections.sort(dataGridResources); + return dataGridResources; + } + + @Override + public List findFirstLevelResources() throws DataGridConnectionRefusedException { + logger.info("Find all first level resources in the grid"); + + List firstLevelResources = new ArrayList<>(); + List allResources = findAll(); + + for (DataGridResource resc : allResources) { + if (resc.isFirstLevelResc()) { + firstLevelResources.add(resc); + } + } + + return firstLevelResources; + } + + @Override + public DataGridResource find(String resourceName) throws DataGridConnectionRefusedException { + logger.info("Find specific resource by name"); + + if (resourceName == null || resourceName.isEmpty()) { + return null; + } + + DataGridResource dgResc = null; + + List resources = findAll(); + + for (DataGridResource r : resources) { + if (resourceName.equals(r.getName())) { + dgResc = r; + break; + } + } + + return dgResc; + } + + private DataGridResource getDataGridResource(Resource irodsResource) { + long irodsResourceId = Long.valueOf(irodsResource.getId()); + String irodsResourceName = irodsResource.getName(); + String irodsResourceZone = irodsResource.getZone().getZoneName(); + String irodsResourceType = irodsResource.getType(); + String irodsResourcePath = irodsResource.getVaultPath(); + long irodsResourceFreeSpace = irodsResource.getFreeSpace(); + Date irodsResourceFreeSpaceTimeStamp = irodsResource.getFreeSpaceTime(); + List irodsResourceChildren = irodsResource.getImmediateChildren(); + String irodsResourceParent = irodsResource.getParentName(); + String irodsResourceStatus = irodsResource.getStatus(); + String irodsResourceHost = irodsResource.getLocation(); + Date irodsResourceCreateTime = irodsResource.getCreateTime(); + Date irodsResourceModifyTime = irodsResource.getModifyTime(); + String irodsResourceInfo = irodsResource.getInfo(); + String irodsContextString = irodsResource.getContextString(); + int irodsResourceTotalRecords = irodsResource.getTotalRecords(); + + if (irodsResourceParent == null || irodsResourceParent.isEmpty()) { + irodsResourceParent = irodsServices.getCurrentUserZone(); + } + + if (irodsResourceType == null || irodsResourceType.isEmpty()) { + irodsResourceType = irodsContextString; + } + + DataGridResource newDataGridResource = new DataGridResource(irodsResourceId, irodsResourceName, irodsResourceZone, irodsResourceType, + irodsResourcePath, irodsResourceFreeSpace, irodsResourceFreeSpaceTimeStamp, irodsResourceChildren, irodsResourceParent, + irodsResourceStatus, irodsResourceHost, irodsResourceCreateTime, irodsResourceModifyTime, irodsResourceInfo, + irodsResourceTotalRecords, irodsContextString); + + return newDataGridResource; + + } + + @Override + public List listResourceTypes() { + + List dataGridResourceTypes = new ArrayList(); + dataGridResourceTypes.add(new DataGridResourceType("Replication", "replication", DataGridResourceTypeEnum.IRODS_COORDINATING)); + dataGridResourceTypes.add(new DataGridResourceType("Round Robin", "roundrobin", DataGridResourceTypeEnum.IRODS_COORDINATING)); + dataGridResourceTypes.add(new DataGridResourceType("Load Balanced", "load_balanced", DataGridResourceTypeEnum.IRODS_COORDINATING)); + dataGridResourceTypes.add(new DataGridResourceType("Compound", "compound", DataGridResourceTypeEnum.IRODS_COORDINATING)); + dataGridResourceTypes.add(new DataGridResourceType("Random", "random", DataGridResourceTypeEnum.IRODS_COORDINATING)); + dataGridResourceTypes.add(new DataGridResourceType("Passthru", "passthru", DataGridResourceTypeEnum.IRODS_COORDINATING)); + + dataGridResourceTypes.add(new DataGridResourceType("Unix File System", "unixfilesystem", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("Universal Mass Storage", "univmss", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("MSO", "mso", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("MSSOFile", "mssofile", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("Mockarchive", "mockarchive", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("Non-blocking", "nonblocking", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("Deferred", "deferred", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("Struct file", "structfile", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("EMC Isilon", "isilon", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("EMC ECS", "ecs", DataGridResourceTypeEnum.IRODS_STORAGE)); + dataGridResourceTypes.add(new DataGridResourceType("WOS", "wos", DataGridResourceTypeEnum.IRODS_STORAGE)); + + Collections.sort(dataGridResourceTypes); + return dataGridResourceTypes; + } + + @Override + public List getImmediateChildren(String resourceName) throws DataGridConnectionRefusedException { + + try { + ResourceAO resourceAO = irodsServices.getResourceAO(); + + Resource irodsResource = resourceAO.findByName(resourceName); + + return irodsResource.getImmediateChildren(); + } + catch (JargonException e) { + logger.error("Could not get immediate children of resource " + resourceName + ": ", e); + } + + return new ArrayList(); + } + + @Override + public boolean createResource(DataGridResource newDataGridResource) throws DataGridConnectionRefusedException { + + try { + ResourceAO resourceAO = irodsServices.getResourceAO(); + ZoneAO zoneAO = irodsServices.getZoneAO(); + + // mapping data grid resource to iRODS Resource + Resource irodsResource = new Resource(); + irodsResource.setName(newDataGridResource.getName()); + irodsResource.setType(newDataGridResource.getType()); + irodsResource.setZone(zoneAO.getZoneByName(newDataGridResource.getZone())); + irodsResource.setCreateTime(newDataGridResource.getCreateTime()); + irodsResource.setModifyTime(newDataGridResource.getModifyTime()); + irodsResource.setStatus(newDataGridResource.getStatus()); + irodsResource.setInfo(newDataGridResource.getInfo()); + irodsResource.setParentName(newDataGridResource.getParent()); + irodsResource.setVaultPath(newDataGridResource.getPath()); + irodsResource.setLocation(newDataGridResource.getHost()); + + // context string is not always set + if (newDataGridResource.getContextString() != null) { + irodsResource.setContextString(newDataGridResource.getContextString()); + } + + // adding the new resource to iRODS + resourceAO.addResource(irodsResource); + + return true; + + } + catch (JargonException e) { + logger.error("Could not create resource: ", e); + } + + return false; + } + + @Override + public boolean deleteResource(String resourceName) { + if (resourceName.isEmpty()) { + logger.error("Could not delete resource: name cannot be empty."); + return false; + } + + boolean isResourceDeleted = true; + + try { + DataGridResource dgRescToRemove = find(resourceName); + + if (dgRescToRemove == null) { + logger.error("Could not delete resource: resource {} not found.", resourceName); + return false; + } + + ResourceAO resourceAO = irodsServices.getResourceAO(); + + // if resource has a parent, it is necessary remove the relationship between that resource and its parent + if (!dgRescToRemove.isFirstLevelResc()) { + resourceAO.removeChildFromResource(dgRescToRemove.getParent(), resourceName); + } + + deleteChildrenFromResource(dgRescToRemove); + + resourceAO.deleteResource(resourceName); + } + catch (Exception e) { + logger.error("Could not delete resource " + resourceName + ": ", e); + isResourceDeleted = false; + } + + return isResourceDeleted; + } + + public void deleteChildrenFromResource(DataGridResource dgRescToRemove) throws DataGridConnectionRefusedException { + ResourceAO resourceAO = irodsServices.getResourceAO(); + + try { + List childrenResources = dgRescToRemove.getChildren(); + if (childrenResources != null && !childrenResources.isEmpty()) { + for (String childResource : childrenResources) { + resourceAO.removeChildFromResource(dgRescToRemove.getName(), childResource); + } + } + } + catch (JargonException e) { + logger.error("Could not delete children from resource {}.", dgRescToRemove.getName()); + } + } + + @Override + public boolean updateResource(String resourceName, List childrenToBeAdded, List childrenToBeRemoved) { + + return false; + } + + @Override + public boolean addChildToResource(String resourceName, String child) { + try { + ResourceAO resourceAO = irodsServices.getResourceAO(); + + // adding new children to the resource + resourceAO.addChildToResource(resourceName, child, ""); + + return true; + } + catch (Exception e) { + logger.error("Could not add children to the " + resourceName + " resource: ", e); + } + + return false; + } + + @Override + public List getAllResourceServers(List resources) throws DataGridConnectionRefusedException { + + logger.info("Getting all resource servers"); + + List servers = new ArrayList<>(); + boolean isResourceWithEmptyHost = false; + + for (DataGridResource resource : resources) { + + logger.debug("Listing resource information: {}", resource); + + if (resource.getContextString().contains("isi_host")) { + continue; + } + + else if (!resource.getHost().isEmpty()) { + + DataGridServer server = new DataGridServer(); + + try { + server.setHostname(resource.getHost()); + server.setIp(machineInfoService.getAddress(resource.getHost())); + server.setResources(getResourcesOfAServer(server.getHostname(), resources)); + } + catch (UnknownHostException e) { + logger.error("Could not retrieve IP address for [{}]", resource.getHost()); + isResourceWithEmptyHost = true; + } + catch (DataGridConnectionRefusedException e) { + logger.error("Could not get all resources of the server: ", resource.getHost()); + server.setResources(null); + } + + if (!isResourceWithEmptyHost && !servers.contains(server)) { + servers.add(server); + } + } + + isResourceWithEmptyHost = false; + } + + Collections.sort(servers); + + return servers; + } + + @Override + public List getAllIsilonServers(List resources) { + logger.info("Getting all isilon servers"); + + List isilonServers = new ArrayList<>(); + + for (DataGridResource resource : resources) { + logger.debug("Listing resource information: {}", resource); + + if (resource.getContextString().contains("isi_host")) { + + DataGridServer isilonServer = new DataGridServer(); + + String isilonHostIp = getIsilonProperties(resource.getContextString()).get("isi_host"); + + if (isilonHostIp == null) { + isilonHostIp = new String(""); + } + + isilonServer.setHostname(isilonHostIp); + isilonServer.setIp(isilonHostIp); + isilonServer.setType(DataGridServerType.ISILON); + + if (!isilonServers.contains(isilonServer)) { + isilonServers.add(isilonServer); + } + } + } + + Collections.sort(isilonServers); + + logger.info("Isilon servers done"); + + return isilonServers; + } + + @Override + public List getResourcesOfAServer(String serverName) throws DataGridConnectionRefusedException { + logger.info("Get resources of a specific server (NOT the using cache)"); + return getResourcesOfAServer(serverName, null); + } + + @Override + public List getResourcesOfAServer(String serverName, List dataGridResources) + throws DataGridConnectionRefusedException { + logger.info("Get resources of a specific server (using cache)"); + + if (dataGridResources == null || dataGridResources.isEmpty()) { + logger.info("No cache provided. Calling the grid."); + dataGridResources = findAll(); + } + + List dataGridResourcesOfAServer = new ArrayList(); + + for (DataGridResource dataGridResource : dataGridResources) { + if (dataGridResource.getHost().compareTo(serverName) == 0) { + dataGridResourcesOfAServer.add(dataGridResource); + } + } + + return dataGridResourcesOfAServer; + } + + private HashMap getIsilonProperties(String contextString) { + HashMap properties = new HashMap(); + + String parts[] = contextString.split(";"); + for (String property : parts) { + String propertyParts[] = property.split("="); + if (propertyParts.length == 2) { + properties.put(propertyParts[0], propertyParts[1]); + } + else { + properties.put(propertyParts[0], new String("")); + } + } + + return properties; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/RuleDeploymentServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/RuleDeploymentServiceImpl.java new file mode 100755 index 000000000..256edf841 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/RuleDeploymentServiceImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.*; +import org.apache.commons.io.FilenameUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.Stream2StreamAO; +import org.irods.jargon.core.pub.domain.Resource; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +@Service +@Transactional +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES) +public class RuleDeploymentServiceImpl implements RuleDeploymentService { + + private static final Logger logger = LoggerFactory.getLogger(RuleDeploymentServiceImpl.class); + + private static final int BUFFER_SIZE = 4 * 1024 * 1024; + private static final String RULE_CACHE_DIR_NAME = ".rulecache"; + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private FileOperationService fos; + + @Autowired + private ConfigService configService; + + @Autowired + private RuleService ruleService; + + @Autowired + private CollectionService collectionService; + + @Override + public void deployRule(MultipartFile file) throws DataGridException { + logger.info("Deploying rule"); + + if (file == null) { + logger.error("File could not be sent to the data grid. Rule file is null."); + throw new DataGridException("Rule file is null."); + } + + if (!ruleCacheExists()) { + logger.info("Rule cache does not exist. Creating one."); + createRuleCache(); + } + + InputStream inputStream; + try { + inputStream = file.getInputStream(); + } catch (IOException e) { + logger.error("Could not get input stream from rule file: ", e.getMessage()); + throw new DataGridException("Could not get input stream from ruleFile."); + } + + // Getting DataObjectAO in order to create the new rule file + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + Stream2StreamAO stream2StreamA0 = irodsServices.getStream2StreamAO(); + IRODSFile targetFile = null; + + try { + String ruleCacheDirPath = getRuleCachePath(); + String ruleName = file.getOriginalFilename().isEmpty() ? file.getName() : file.getOriginalFilename(); + targetFile = irodsFileFactory.instanceIRODSFile(ruleCacheDirPath, ruleName); + + stream2StreamA0.transferStreamToFileUsingIOStreams(inputStream, (File) targetFile, 0, BUFFER_SIZE); + + String resourceName = irodsServices.getDefaultStorageResource(); + Resource resc = irodsServices.getResourceAO().findByName(resourceName); + String vaultPath = resc.getVaultPath(); + String host = resc.getLocation(); + + String ruleVaultPath = String.format("%s/%s/%s", vaultPath, RULE_CACHE_DIR_NAME, ruleName); + + String ruleNameWithoutExtension = FilenameUtils.removeExtension(ruleName); + ruleService.execDeploymentRule(host, ruleNameWithoutExtension, ruleVaultPath); + } catch (JargonException e) { + if (targetFile != null) fos.deleteDataObject(targetFile.getPath(), true); + logger.error("Upload stream failed from Metalnx to the data grid. {}", e.getMessage()); + throw new DataGridException("Upload failed. Resource(s) might be full."); + } finally { + try { + inputStream.close(); // Closing streams opened + } catch (IOException e) { + logger.error("Could close stream: ", e.getMessage()); + } + } + } + + @Override + public String getRuleCachePath() { + return String.format("/%s/%s", configService.getIrodsZone(), RULE_CACHE_DIR_NAME); + } + + @Override + public void createRuleCache() throws DataGridException { + String parentPath = String.format("/%s", configService.getIrodsZone()); + DataGridCollectionAndDataObject ruleCacheDir = + new DataGridCollectionAndDataObject(getRuleCachePath(), parentPath, true); + collectionService.createCollection(ruleCacheDir); + } + + @Override + public boolean ruleCacheExists() { + return collectionService.isPathValid(getRuleCachePath()); + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/RuleServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/RuleServiceImpl.java new file mode 100755 index 000000000..66d60f915 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/RuleServiceImpl.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import java.util.List; +import java.util.Map; + +import org.irods.jargon.core.exception.FileNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.rule.IRODSRuleExecResult; +import org.irods.jargon.core.rule.IRODSRuleExecResultOutputParameter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridRule; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; +import com.emc.metalnx.core.domain.utils.DataGridCoreUtils; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.ConfigService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.ResourceService; +import com.emc.metalnx.services.interfaces.RuleService; + +@Service("ruleService") +@Transactional +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES) +public class RuleServiceImpl implements RuleService { + + private static final Logger logger = LoggerFactory.getLogger(RuleServiceImpl.class); + + @Autowired + CollectionService cs; + + @Autowired + private IRODSServices is; + + @Autowired + private ResourceService rs; + + @Autowired + private ConfigService configService; + + @Override + public void execReplDataObjRule(String destResc, String path, boolean inAdminMode) + throws DataGridRuleException, DataGridConnectionRefusedException { + logger.info("Get Replication Rule called"); + + String flags = String.format("destRescName=%s%s", destResc, inAdminMode ? "++++irodsAdmin=" : ""); + + DataGridResource dgResc = rs.find(destResc); + DataGridRule rule = new DataGridRule(DataGridRule.REPL_DATA_OBJ_RULE, dgResc.getHost()); + rule.setInputRuleParams(path, flags, "null"); + + executeRule(rule.toString()); + } + + @Override + public void execPopulateMetadataRule(String host, String objPath) + throws DataGridRuleException, DataGridConnectionRefusedException { + if (!configService.isPopulateMsiEnabled()) + return; + + logger.info("Get Populate Rule called"); + + DataGridRule rule = new DataGridRule(DataGridRule.POPULATE_RULE, host); + rule.setInputRuleParams(objPath); + + executeRule(rule.toString()); + } + + @Override + public void execImageRule(String host, String objPath, String filePath) + throws DataGridRuleException, DataGridConnectionRefusedException { + if (!DataGridCoreUtils.isImageFile(objPath)) + return; + + logger.info("Get Image Rule called"); + + DataGridRule rule = new DataGridRule(DataGridRule.JPG_RULE, host); + rule.setInputRuleParams(objPath, filePath); + + executeRule(rule.toString()); + } + + @Override + public void execVCFMetadataRule(String host, String objPath, String filePath) + throws DataGridRuleException, DataGridConnectionRefusedException { + if (!DataGridCoreUtils.isVCFFile(objPath)) + return; + + DataGridRule rule = new DataGridRule(DataGridRule.VCF_RULE, host); + rule.setInputRuleParams(objPath, filePath); + + executeRule(rule.toString()); + } + + @Override + public void execBamCramMetadataRule(String host, String objPath, String filePath) + throws DataGridRuleException, DataGridConnectionRefusedException { + if (!DataGridCoreUtils.isBamOrCram(objPath)) + return; + + logger.info("Get BAM/CRAM Rule called"); + + DataGridRule rule = new DataGridRule(DataGridRule.BAM_CRAM_RULE, host); + rule.setInputRuleParams(objPath, filePath); + + executeRule(rule.toString()); + } + + @Override + public void execManifestFileRule(String host, String targetPath, String objPath, String filePath) + throws DataGridRuleException, DataGridConnectionRefusedException, FileNotFoundException, JargonException { + if (!DataGridCoreUtils.isPrideXMLManifestFile(objPath)) + return; + + logger.info("Get Manifest Rule called"); + + DataGridRule rule = new DataGridRule(DataGridRule.XML_MANIFEST_RULE, host); + + List objs = cs.getSubCollectionsAndDataObjectsUnderPath(targetPath); + + for (DataGridCollectionAndDataObject obj : objs) { + logger.info("Extracting metadata from [{}] and applying on [{}]", filePath, obj.getPath()); + rule.setInputRuleParams(obj.getPath(), filePath, filePath); + executeRule(rule.toString()); + } + } + + @Override + public List execGetMSIsRule(String host) throws DataGridConnectionRefusedException, DataGridRuleException { + logger.info("Get Microservices Rule called"); + + DataGridRule rule = new DataGridRule(DataGridRule.GET_MSIS_RULE, host); + rule.setOutputRuleParams("msis"); + + logger.debug(rule.toString()); + + return DataGridCoreUtils.getMSIsAsList((String) executeRule(rule.toString()).get("*msis").getResultObject()); + } + + @Override + public String execGetVersionRule(String host) throws DataGridRuleException, DataGridConnectionRefusedException { + logger.info("Get Version Rule called"); + + DataGridRule rule = new DataGridRule(DataGridRule.GET_VERSION_RULE, host); + rule.setOutputRuleParams("version"); + + logger.debug(rule.toString()); + + return (String) executeRule(rule.toString()).get("*version").getResultObject(); + } + + @Override + public void execIlluminaMetadataRule(DataGridResource dgResc, String targetPath, String objPath) + throws DataGridRuleException, DataGridConnectionRefusedException { + if (!DataGridCoreUtils.isIllumina(objPath)) + return; + + logger.info("Illumina Rule called"); + + String destResc = dgResc.getName(); + String host = dgResc.getHost(); + boolean declareOutputParams = false; + + DataGridRule tarRule = new DataGridRule(DataGridRule.TAR_RULE, host, declareOutputParams); + tarRule.setInputRuleParams(objPath, targetPath, destResc); + tarRule.setOutputRuleParams("Status"); + + DataGridRule illuminaRule = new DataGridRule(DataGridRule.ILLUMINA_RULE, host, declareOutputParams); + illuminaRule.setInputRuleParams(objPath, destResc); + + executeRule(tarRule.toString()); + executeRule(illuminaRule.toString()); + } + + @Override + public void execEmptyTrashRule(String destResc, String objPath, boolean inAdminMode) + throws DataGridConnectionRefusedException, DataGridRuleException { + logger.info("Empty Trash Rule called"); + + DataGridResource dgResc = rs.find(destResc); + DataGridRule rule = new DataGridRule(DataGridRule.EMPTY_TRASH_RULE, dgResc.getHost(), false); + + String flag = inAdminMode ? "irodsAdminRmTrash=" : "irodsRmTrash="; + + rule.setInputRuleParams(objPath, flag); + rule.setOutputRuleParams("out"); + + executeRule(rule.toString()); + } + + @Override + public void execDeploymentRule(String host, String ruleName, String ruleVaultPath) + throws DataGridRuleException, DataGridConnectionRefusedException { + logger.info("Deploy Rule called"); + + if (ruleName == null || ruleName.isEmpty() || ruleVaultPath == null || ruleVaultPath.isEmpty() + || !is.isAtLeastIrods420()) + return; + + DataGridRule rule = new DataGridRule(DataGridRule.DEPLOYMENT_RULE, host, false); + + rule.setInputRuleParams(ruleName, ruleVaultPath); + executeRule(rule.toString()); + } + + @Override + public Map executeRule(String rule) + throws DataGridRuleException, DataGridConnectionRefusedException { + if (rule == null || rule.isEmpty()) + return null; + + Map ruleResultMap; + + try { + IRODSRuleExecResult result = is.getRuleProcessingAO().executeRule(rule); + ruleResultMap = result.getOutputParameterResults(); + } catch (JargonException e) { + String error = String.format("Could not execute rule %s: %s", rule, e.getMessage()); + logger.error(error); + throw new DataGridRuleException(error); + } + + return ruleResultMap; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/SpecQueryServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/SpecQueryServiceImpl.java new file mode 100755 index 000000000..35d6fba4c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/SpecQueryServiceImpl.java @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import java.util.List; + +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.SpecificQueryAO; +import org.irods.jargon.core.pub.domain.ClientHints; +import org.irods.jargon.core.pub.domain.SpecificQueryDefinition; +import org.irods.jargon.core.query.JargonQueryException; +import org.irods.jargon.core.query.SpecificQuery; +import org.irods.jargon.core.query.SpecificQueryResultSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.emc.metalnx.core.domain.entity.DataGridFilePropertySearch; +import com.emc.metalnx.core.domain.entity.DataGridMetadataSearch; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.UnsupportedDataGridFeatureException; +import com.emc.metalnx.services.interfaces.AdminServices; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.SpecQueryService; +import com.emc.metalnx.services.irods.utils.SpecificQueryProvider; +import com.emc.metalnx.services.irods.utils.SpecificQueryProviderFactory; +import com.emc.metalnx.services.irods.utils.SpecificQueryProviderFactoryImpl; +import com.emc.metalnx.services.machine.util.DataGridUtils; + +@Service +@Transactional +public class SpecQueryServiceImpl implements SpecQueryService { + @Autowired + AdminServices adminServices; + + @Autowired + IRODSServices irodsServices; + + private static final Logger logger = LoggerFactory.getLogger(SpecQueryServiceImpl.class); + /* + * This will switch based on the iCat to create sql statements for various back + * end databases + */ + private final SpecificQueryProviderFactory specificQueryProviderFactory = new SpecificQueryProviderFactoryImpl(); + + @Override + public int countCollectionsMatchingMetadata(List metadataSearch, String zone) + throws DataGridConnectionRefusedException, JargonException { + return countItemsMatchingMetadata(metadataSearch, zone, true); + } + + @Override + public int countDataObjectsMatchingMetadata(List metadataSearch, String zone) + throws DataGridConnectionRefusedException, JargonException { + return countItemsMatchingMetadata(metadataSearch, zone, false); + } + + @Override + public void deleteSpecQueryByAlias(String specQueryAlias) throws DataGridConnectionRefusedException { + + SpecificQueryAO specificQueryAO = null; + + try { + specificQueryAO = adminServices.getSpecificQueryAO(); + + specificQueryAO.removeSpecificQueryByAlias(specQueryAlias); + } catch (JargonException e) { + logger.error("Could not get specific query: ", e.getMessage()); + } + } + + @Override + public SpecificQueryResultSet searchByMetadata(List metadataSearch, String zone, + boolean searchAgainstColls, DataGridPageContext pageContext, int offset, int limit) + throws DataGridConnectionRefusedException, JargonException { + + SpecificQueryAO specificQueryAO = null; + SpecificQuery specQuery = null; + SpecificQueryResultSet queryResultSet = null; + String userSQLAlias = "metalnxUserQuery_" + System.currentTimeMillis(); + + try { + specificQueryAO = adminServices.getSpecificQueryAO(); + + ClientHints clientHints = this.irodsServices.getEnvironmentalInfoAO().retrieveClientHints(false); + SpecificQueryProvider provider = specificQueryProviderFactory.instance(clientHints.whatTypeOfIcatIsIt()); + String query = provider.buildSpecificQueryForMetadataSearch(metadataSearch, zone, searchAgainstColls); + + // Creating Specific Query instance + SpecificQueryDefinition queryDef = new SpecificQueryDefinition(); + queryDef.setAlias(userSQLAlias); + queryDef.setSql(query); + + // Creating spec query on iRODS + specificQueryAO.addSpecificQuery(queryDef); + + specQuery = SpecificQuery.instanceWithNoArguments(userSQLAlias, 0, zone); + + logger.info("Specific query: {}", query.toString()); + + queryResultSet = specificQueryAO.executeSpecificQueryUsingAlias(specQuery, 99999, 0); + } catch (JargonException e) { + logger.error("Could not get specific query: ", e); + throw e; + } catch (JargonQueryException e) { + logger.error("Could not get specific query: ", e); + throw new JargonException(e); + } catch (UnsupportedDataGridFeatureException e) { + logger.error("Could not get specific query: ", e); + throw new JargonException(e); + } finally { + try { + // after running the user specific query, we need to remove from the database + specificQueryAO.removeSpecificQueryByAlias(userSQLAlias); + } catch (JargonException e) { + logger.error("Could not remove specific query {}: ", userSQLAlias, e.getMessage()); + } + } + + return queryResultSet; + } + + /** + * Counts the number of items matching a metadata search criteria. + * + * @param metadataSearch + * metadata criteria + * @param zone + * zone name + * @param searchAgainstColls + * flag set to true when searching collections and false when + * searching data data objects + * @return total number of items matching a metadata search criteria + * @throws DataGridConnectionRefusedException + * @throws JargonException + */ + private int countItemsMatchingMetadata(List metadataSearch, String zone, + boolean searchAgainstColls) throws DataGridConnectionRefusedException, JargonException { + + int totalItems = 0; + + SpecificQueryAO specificQueryAO = null; + SpecificQuery specQuery = null; + SpecificQueryResultSet queryResultSet = null; + String userSQLAlias = "metalnxUserQuery_" + System.currentTimeMillis(); + + try { + specificQueryAO = adminServices.getSpecificQueryAO(); + + ClientHints clientHints = this.irodsServices.getEnvironmentalInfoAO().retrieveClientHints(false); + SpecificQueryProvider provider = specificQueryProviderFactory.instance(clientHints.whatTypeOfIcatIsIt()); + String query = provider.buildQueryForCountOfItemsMatchingMetadataSearch(metadataSearch, zone, + searchAgainstColls); + + // Creating Specific Query instance + SpecificQueryDefinition queryDef = new SpecificQueryDefinition(); + queryDef.setAlias(userSQLAlias); + queryDef.setSql(query); + + // Creating spec query on iRODS + specificQueryAO.addSpecificQuery(queryDef); + specQuery = SpecificQuery.instanceWithNoArguments(userSQLAlias, 0, zone); + logger.info("Specific query: {}", query.toString()); + + queryResultSet = specificQueryAO.executeSpecificQueryUsingAlias(specQuery, 99999, 0); + + // after running the user specific query, we need to remove from the database + specificQueryAO.removeSpecificQueryByAlias(userSQLAlias); + + totalItems = DataGridUtils.mapCountQueryResultSetToInteger(queryResultSet); + } catch (JargonException e) { + logger.error("Could not get specific query: ", e); + throw e; + } catch (JargonQueryException e) { + logger.error("Could not get specific query: ", e); + throw new JargonException(e); + } catch (UnsupportedDataGridFeatureException e) { + logger.error("Could not get specific query: ", e); + throw new JargonException(e); + } + + return totalItems; + } + + /** + * Counts the number of items matching a file properties search criteria. + * + * @param filePropertiesSearch + * filePropertiesSearch criteria + * @param zone + * zone name + * @param searchAgainstColls + * flag set to true when searching collections and false when + * searching data data objects + * @return total number of items matching a file properties search criteria + * @throws DataGridConnectionRefusedException + * @throws UnsupportedDataGridFeatureException + * @throws JargonException + */ + private int countItemsMatchingFileProperties(List filePropertiesSearch, String zone, + boolean searchAgainstColls) + throws DataGridConnectionRefusedException, UnsupportedDataGridFeatureException, JargonException { + + logger.info("countItemsMatchingFileProperties()"); + int totalItems = 0; + + SpecificQueryAO specificQueryAO = null; + SpecificQuery specQuery = null; + SpecificQueryResultSet queryResultSet = null; + String userSQLAlias = "metalnxUserQuery_" + System.currentTimeMillis(); + + try { + specificQueryAO = adminServices.getSpecificQueryAO(); + + ClientHints clientHints = this.irodsServices.getEnvironmentalInfoAO().retrieveClientHints(false); + SpecificQueryProvider provider = specificQueryProviderFactory.instance(clientHints.whatTypeOfIcatIsIt()); + String query = provider.buildQueryCountItemsMatchingPropertiesSearch(filePropertiesSearch, zone, + searchAgainstColls); + + logger.debug("query:{}", query); + + // Creating Specific Query instance + SpecificQueryDefinition queryDef = new SpecificQueryDefinition(); + queryDef.setAlias(userSQLAlias); + queryDef.setSql(query); + + // Creating spec query on iRODS + specificQueryAO.addSpecificQuery(queryDef); + specQuery = SpecificQuery.instanceWithNoArguments(userSQLAlias, 0, zone); + + logger.info("Specific query: {}", query.toString()); + + queryResultSet = specificQueryAO.executeSpecificQueryUsingAlias(specQuery, 99999, 0); + + // after running the user specific query, we need to remove from the database + specificQueryAO.removeSpecificQueryByAlias(userSQLAlias); + + totalItems = DataGridUtils.mapCountQueryResultSetToInteger(queryResultSet); + + } catch (JargonException e) { + logger.error("Could not get specific query: ", e); + throw e; + } catch (JargonQueryException e) { + logger.error("Could not get specific query: ", e); + throw new JargonException(e); + } + + return totalItems; + } + + @Override + public SpecificQueryResultSet searchByFileProperties(List filePropertySearch, + String zone, boolean searchAgainstColls, DataGridPageContext pageContext, int offset, int limit) + throws DataGridConnectionRefusedException, JargonException { + + SpecificQueryAO specificQueryAO = null; + SpecificQuery specQuery = null; + SpecificQueryResultSet queryResultSet = null; + String userSQLAlias = "metalnxUserQuery_" + System.currentTimeMillis(); + + try { + specificQueryAO = adminServices.getSpecificQueryAO(); + + ClientHints clientHints = this.irodsServices.getEnvironmentalInfoAO().retrieveClientHints(false); + SpecificQueryProvider provider = specificQueryProviderFactory.instance(clientHints.whatTypeOfIcatIsIt()); + String query = provider.buildQueryForFilePropertiesSearch(filePropertySearch, zone, searchAgainstColls, + offset, limit); + + // Creating Specific Query instance + SpecificQueryDefinition queryDef = new SpecificQueryDefinition(); + queryDef.setAlias(userSQLAlias); + queryDef.setSql(query); + + // Creating spec query on iRODS + specificQueryAO.addSpecificQuery(queryDef); + + specQuery = SpecificQuery.instanceWithNoArguments(userSQLAlias, 0, zone); + + logger.info("Specific query: {}", query); + + queryResultSet = specificQueryAO.executeSpecificQueryUsingAlias(specQuery, 99999, 0); + } catch (JargonException e) { + logger.error("Could not get specific query: ", e); + throw e; + } catch (JargonQueryException e) { + logger.error("Could not get specific query: ", e); + throw new JargonException(e); + } catch (UnsupportedDataGridFeatureException e) { + logger.error("Could not get specific query: ", e); + throw new JargonException(e); + } finally { + try { + // after running the user specific query, we need to remove from the database + specificQueryAO.removeSpecificQueryByAlias(userSQLAlias); + } catch (JargonException e) { + logger.error("Could not remove specific query {}: ", userSQLAlias, e.getMessage()); + } + } + + return queryResultSet; + } + + @Override + public int countCollectionsMatchingFileProperties(List filePropertiesSearch, + String zone) + throws DataGridConnectionRefusedException, UnsupportedDataGridFeatureException, JargonException { + + return countItemsMatchingFileProperties(filePropertiesSearch, zone, true); + } + + @Override + public int countDataObjectsMatchingFileProperties(List filePropertiesSearch, + String zone) + throws DataGridConnectionRefusedException, UnsupportedDataGridFeatureException, JargonException { + + return countItemsMatchingFileProperties(filePropertiesSearch, zone, false); + } + + /** + * @return the irodsServices + */ + public IRODSServices getIrodsServices() { + return irodsServices; + } + + /** + * @param irodsServices + * the irodsServices to set + */ + public void setIrodsServices(IRODSServices irodsServices) { + this.irodsServices = irodsServices; + } + + /** + * @return the adminServices + */ + public AdminServices getAdminServices() { + return adminServices; + } + + /** + * @param adminServices + * the adminServices to set + */ + public void setAdminServices(AdminServices adminServices) { + this.adminServices = adminServices; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/SpecificQueryServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/SpecificQueryServiceImpl.java new file mode 100755 index 000000000..963a695ff --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/SpecificQueryServiceImpl.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.entity.DataGridSpecificQuery; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.SpecificQueryService; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.SpecificQueryAO; +import org.irods.jargon.core.pub.domain.SpecificQueryDefinition; +import org.irods.jargon.core.query.JargonQueryException; +import org.irods.jargon.core.query.SpecificQuery; +import org.irods.jargon.core.query.SpecificQueryResultSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class SpecificQueryServiceImpl implements SpecificQueryService { + + @Autowired + private IRODSServices irodsServices; + + private static final Logger logger = LoggerFactory.getLogger(SpecificQueryServiceImpl.class); + + private final DataGridSpecificQuery createDataGridSpecificQuery(SpecificQueryDefinition specificQueryFromIrods) { + DataGridSpecificQuery newQuery = new DataGridSpecificQuery(); + newQuery.setAlias(specificQueryFromIrods.getAlias()); + newQuery.setQuery(specificQueryFromIrods.getSql()); + return newQuery; + } + + private final List createDataGridSpecificQueryList(List specificQueriesFromIrods) { + List specificQueries = new ArrayList(); + for (SpecificQueryDefinition specificQueryFromIrods : specificQueriesFromIrods) { + DataGridSpecificQuery newQuery = createDataGridSpecificQuery(specificQueryFromIrods); + specificQueries.add(newQuery); + } + return specificQueries; + } + + @Override + public List findAll() throws DataGridConnectionRefusedException { + return findByAliasLike("%"); + } + + @Override + public DataGridSpecificQuery findByAlias(String alias) throws DataGridConnectionRefusedException { + SpecificQueryAO specificQueryAO = irodsServices.getSpecificQueryAO(); + SpecificQueryDefinition query; + try { + query = specificQueryAO.findSpecificQueryByAlias(alias); + return createDataGridSpecificQuery(query); + } catch (JargonException e) { + logger.error("Could not retrieve specific query with alias {}", alias, e); + } + return null; + } + + @Override + public List findByAliasLike(String like) + throws DataGridConnectionRefusedException { + + SpecificQueryAO specificQueryAO = irodsServices.getSpecificQueryAO(); + + try { + List specificQueriesFromIrods = specificQueryAO.listSpecificQueryByAliasLike(like); + return createDataGridSpecificQueryList(specificQueriesFromIrods); + } catch (Exception e) { + logger.error("Could not find any specific query definition", e); + } + + return null; + } + + @Override + public SpecificQueryResultSet executeSpecificQuery(DataGridSpecificQuery specificQuery, + String zone) throws DataGridConnectionRefusedException { + + SpecificQueryAO specificQueryAO = irodsServices.getSpecificQueryAO(); + + try { + SpecificQuery query = SpecificQuery.instanceWithNoArguments(specificQuery.getQuery(), 0, zone); + return specificQueryAO.executeSpecificQueryUsingSql(query, 1000); + } catch (JargonException | JargonQueryException e) { + logger.error("Could not execute specific query {}", specificQuery.getAlias(), e); + } + + return null; + } + + @Override + public boolean createSpecificQuery(DataGridSpecificQuery specificQuery) + throws DataGridConnectionRefusedException { + + SpecificQueryAO specificQueryAO = irodsServices.getSpecificQueryAO(); + try { + SpecificQueryDefinition newQuery = new SpecificQueryDefinition(specificQuery.getAlias(), specificQuery.getQuery()); + specificQueryAO.addSpecificQuery(newQuery); + return true; + } catch (JargonException e) { + logger.error("Could not create specific query {}", specificQuery.getAlias(), e); + } + + return false; + } + + @Override + public boolean updateSpecificQuery(DataGridSpecificQuery specificQuery) + throws DataGridConnectionRefusedException { + + SpecificQueryAO specificQueryAO = irodsServices.getSpecificQueryAO(); + try { + + // Jargon does not support SpecificQueries update, so we'll remove and create + // a new one. + specificQueryAO.removeSpecificQueryByAlias(specificQuery.getAlias()); + this.createSpecificQuery(specificQuery); + return true; + } catch (JargonException e) { + logger.error("Could not create specific query {}", specificQuery.getAlias(), e); + } + return false; + } + + @Override + public boolean removeSpecificQueryByAlias(DataGridSpecificQuery specificQuery) + throws DataGridConnectionRefusedException { + + SpecificQueryAO specificQueryAO = irodsServices.getSpecificQueryAO(); + try { + specificQueryAO.removeSpecificQueryByAlias(specificQuery.getAlias()); + return true; + } catch (JargonException e) { + logger.error("Could not remove specific query {}", specificQuery.getAlias(), e); + } + return false; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TemplateFieldServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TemplateFieldServiceImpl.java new file mode 100755 index 000000000..2ce708baa --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TemplateFieldServiceImpl.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.dao.TemplateFieldDao; +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateAttrException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateUnitException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateValueException; +import com.emc.metalnx.services.interfaces.TemplateFieldService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +public class TemplateFieldServiceImpl implements TemplateFieldService { + + @Autowired + private TemplateFieldDao templateFieldDao; + + private static final Logger logger = LoggerFactory.getLogger(TemplateFieldServiceImpl.class); + + @Override + public DataGridTemplateField findById(long id) { + logger.info("Find template field by ID {}", id); + return templateFieldDao.findById(id); + } + + @Override + public long createTemplateField(DataGridTemplateField dataGridTemplateField) { + if (dataGridTemplateField == null) { + return 0; + } + + logger.info("Creating template field {}, {}, {} for template {}", dataGridTemplateField.getAttribute(), dataGridTemplateField.getValue(), + dataGridTemplateField.getUnit(), dataGridTemplateField.getTemplate().getTemplateName()); + + return templateFieldDao.save(dataGridTemplateField); + } + + @Override + public List findAll() { + logger.info("Find all template fields."); + return templateFieldDao.findAll(DataGridTemplateField.class); + } + + @Override + public boolean deleteTemplateField(DataGridTemplateField dataGridTemplateField) { + if (dataGridTemplateField == null) { + return false; + } + + logger.info("Delete template field {}", dataGridTemplateField.getAttribute(), dataGridTemplateField.getValue(), + dataGridTemplateField.getUnit(), dataGridTemplateField.getTemplate().getTemplateName()); + + templateFieldDao.delete(dataGridTemplateField); + + return true; + } + + @Override + public boolean modifyTemplateField(long id, String attribute, String value, String unit) throws DataGridTemplateAttrException, + DataGridTemplateValueException, DataGridTemplateUnitException { + logger.info("Modify template field by id {}", id); + return templateFieldDao.modifyById(id, attribute, value, unit); + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TemplateServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TemplateServiceImpl.java new file mode 100755 index 000000000..f68e48322 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TemplateServiceImpl.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.com.emc.metalnx.core.xml.MlxMetadataAVU; +import com.emc.com.emc.metalnx.core.xml.MlxMetadataTemplate; +import com.emc.com.emc.metalnx.core.xml.MlxMetadataTemplates; +import com.emc.metalnx.core.domain.dao.TemplateDao; +import com.emc.metalnx.core.domain.dao.TemplateFieldDao; +import com.emc.metalnx.core.domain.entity.DataGridTemplate; +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateAttrException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateUnitException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateValueException; +import com.emc.metalnx.core.domain.exceptions.DataGridTooLongTemplateNameException; +import com.emc.metalnx.services.interfaces.TemplateService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.InputStream; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; + +@Service +@Transactional +public class TemplateServiceImpl implements TemplateService { + + @Autowired + private TemplateDao templateDao; + + @Autowired + private TemplateFieldDao templateFieldDao; + + private static final Logger logger = LoggerFactory.getLogger(TemplateServiceImpl.class); + + @Override + public boolean modifyTemplate(DataGridTemplate template) { + if (template == null) { + return false; + } + + templateDao.merge(template); + + return true; + } + + @Override + public DataGridTemplate findById(long id) { + return templateDao.findById(id); + } + + @Override + public DataGridTemplate findByName(String templateName) { + return templateDao.findByName(templateName); + } + + @Override + public long createTemplate(DataGridTemplate dataGridTemplate) { + Date today = new Date(); + + dataGridTemplate.setVersion(1); + dataGridTemplate.setCreateTs(today); + dataGridTemplate.setModifyTs(today); + + long id = templateDao.save(dataGridTemplate); + + return id; + } + + @Override + public List findAll() { + List dataGridTemplates = templateDao.findAll(DataGridTemplate.class); + + Collections.sort(dataGridTemplates); + + return dataGridTemplates; + } + + @Override + public boolean deleteTemplate(long id) { + /* + * we need to remove all template fields existing in a template before + * removing the Template itself + */ + List templateFields = this.listTemplateFields(id); + for (DataGridTemplateField templateField : templateFields) { + templateFieldDao.delete(templateField); + } + + return templateDao.deleteById(id); + } + + @Override + public List findByQueryString(String queryString) { + List templates = templateDao.findByQueryString(queryString); + Collections.sort(templates); + return templates; + } + + @Override + public List listTemplateFields(String template) { + List templateFields = templateDao.listTemplateFields(template); + Collections.sort(templateFields); + return templateFields; + } + + @Override + public List listTemplateFields(Long id) { + List templateFields = templateDao.listTemplateFields(id); + Collections.sort(templateFields); + return templateFields; + } + + @Override + public List listPublicTemplates() { + return templateDao.listPublicTemplates(); + } + + @Override + public List listPrivateTemplatesByUser(String user) { + return templateDao.listPrivateTemplatesByUser(user); + } + + @Override + public boolean importXmlMetadataTemplate(InputStream inStream, String owner, String prefix, String suffix) throws JAXBException, + DataGridTooLongTemplateNameException, DataGridTemplateAttrException, DataGridTemplateValueException, DataGridTemplateUnitException { + + JAXBContext jaxbContext = JAXBContext.newInstance(MlxMetadataTemplates.class); + Unmarshaller un = jaxbContext.createUnmarshaller(); + MlxMetadataTemplates ts = (MlxMetadataTemplates) un.unmarshal(inStream); + + boolean result = true; + + for (MlxMetadataTemplate t : ts.getTemplates()) { + + String newTemplateName = String.format("%s%s%s", prefix, t.getName(), suffix); + + if (findByName(newTemplateName) != null) { + logger.info("Template with name {} already exists on the database", newTemplateName); + result = false; + continue; + } + + DataGridTemplate nt = new DataGridTemplate(); + nt.setTemplateName(newTemplateName); + nt.setDescription(t.getDescription()); + nt.setUsageInformation(t.getUsageInfo()); + nt.setAccessType(t.getAccessType()); + nt.setOwner(owner); + + nt.setFields(new HashSet()); + long tid = createTemplate(nt); + nt.setId(tid); + + for (MlxMetadataAVU a : t.getMetadatas()) { + DataGridTemplateField na = new DataGridTemplateField(); + na.setAttribute(a.getAttribute()); + na.setValue(a.getValue()); + na.setUnit(a.getUnit()); + na.setTemplate(nt); + templateFieldDao.save(na); + } + } + return result; + } + + @Override + public MlxMetadataTemplate mapDataGridTemplateToXml(DataGridTemplate template) { + // Mapping DB entity to XML entity + MlxMetadataTemplate t = new MlxMetadataTemplate(); + t.setName(template.getTemplateName()); + t.setDescription(template.getDescription()); + t.setUsageInfo(template.getUsageInformation()); + t.setAccessType(template.getAccessType()); + + for (DataGridTemplateField field : template.getFields()) { + MlxMetadataAVU avu = new MlxMetadataAVU(); + avu.setAttribute(field.getAttribute()); + avu.setValue(field.getValue()); + avu.setUnit(field.getUnit()); + t.getMetadatas().add(avu); + } + + return t; + } + + @Override + public int countAll() { + int count = templateDao.findAll(DataGridTemplate.class).size(); + + return count; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TicketClientServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TicketClientServiceImpl.java new file mode 100755 index 000000000..1c0b444d2 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TicketClientServiceImpl.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.exceptions.DataGridTicketDownloadException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.auth.UserTokenDetails; +import com.emc.metalnx.services.interfaces.ConfigService; +import com.emc.metalnx.services.interfaces.TicketClientService; +import com.emc.metalnx.services.interfaces.ZipService; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.connection.IRODSAccount; +import org.irods.jargon.core.exception.*; +import org.irods.jargon.core.pub.IRODSAccessObjectFactory; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.irods.jargon.ticket.TicketClientOperations; +import org.irods.jargon.ticket.TicketServiceFactory; +import org.irods.jargon.ticket.TicketServiceFactoryImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import javax.annotation.PostConstruct; +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +@Service +@Transactional +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES) +public class TicketClientServiceImpl implements TicketClientService { + private static final Logger logger = LoggerFactory.getLogger(TicketClientServiceImpl.class); + private static final String TEMP_TICKET_DIR = "tmp-ticket-files"; + private static final String RESOURCE = ""; + private static final String ANONYMOUS_HOME_DIRECTORY = ""; + + @Autowired + private ConfigService configService; + + @Autowired + private ZipService zipService; + + @Autowired + private IRODSAccessObjectFactory irodsAccessObjectFactory; + + private TicketClientOperations ticketClientOperations; + private IRODSAccount irodsAccount; + private String host, zone; + private int port; + private Map ticketErroCodeMap; + + @PostConstruct + public void init() { + host = configService.getIrodsHost(); + zone = configService.getIrodsZone(); + port = Integer.valueOf(configService.getIrodsPort()); + ticketErroCodeMap = new HashMap<>(); + ticketErroCodeMap.put(-891000, "Ticket expired"); + ticketErroCodeMap.put(-892000, "Ticket uses exceeded"); + ticketErroCodeMap.put(-893000, "Ticket user excluded"); + ticketErroCodeMap.put(-894000, "Ticket host excluded"); + ticketErroCodeMap.put(-895000, "Ticket group excluded"); + ticketErroCodeMap.put(-896000, "Ticket write uses exceeded"); + ticketErroCodeMap.put(-526020, "Destination not a directory"); + } + + @Override + public void transferFileToIRODSUsingTicket(String ticketString, File localFile, String destPath) + throws DataGridTicketUploadException, DataGridTicketInvalidUserException { + + if (ticketString == null || ticketString.isEmpty()) { + throw new DataGridTicketUploadException("Ticket String not provided"); + } else if (destPath == null || destPath.isEmpty()) { + throw new DataGridTicketUploadException("Ticket path not provided"); + } else if (localFile == null) { + throw new DataGridTicketUploadException("File not provided"); + } + + try { + setUpAccess(); + + IRODSFileFactory irodsFileFactory = irodsAccessObjectFactory.getIRODSFileFactory(irodsAccount); + String targetPath = String.format("%s/%s", destPath, localFile.getName()); + IRODSFile targetFile = irodsFileFactory.instanceIRODSFile(targetPath); + ticketClientOperations.putFileToIRODSUsingTicket(ticketString, localFile, targetFile, null, null); + } catch (InvalidUserException e) { + logger.error("Invalid user. Cannot download files as anonymous."); + throw new DataGridTicketInvalidUserException("Invalid user anonymous"); + } catch (OverwriteException | DuplicateDataException e) { + logger.error("Could not transfer file to the grid. File already exists: {}", e); + throw new DataGridTicketUploadException("File already exists"); + } catch(CatNoAccessException e) { + logger.error("Could not transfer file to the grid. Cat no access: {}", e); + throw new DataGridTicketUploadException(e.getMessage()); + } catch (DataNotFoundException e) { + logger.error("Could not transfer file to the grid. File not found: {}", e); + throw new DataGridTicketUploadException("File not found"); + } catch (JargonException e) { + logger.error("Could not transfer file to the grid using a ticket: {}", e); + int code = e.getUnderlyingIRODSExceptionCode(); + String msg = "Transfer failed"; + if (ticketErroCodeMap.containsKey(code)) { + msg = ticketErroCodeMap.get(code); + } + throw new DataGridTicketUploadException(msg); + } finally { + closeAccess(); + FileUtils.deleteQuietly(localFile); + } + } + + @Override + public File getFileFromIRODSUsingTicket(String ticketString, String path) + throws DataGridTicketInvalidUserException, DataGridTicketDownloadException { + + deleteTempTicketDir(); + + File tempDir = new File(TEMP_TICKET_DIR); + + if (!tempDir.exists()) { + tempDir.mkdir(); + } + + File file; + try { + setUpAccess(); + + IRODSFileFactory irodsFileFactory = irodsAccessObjectFactory.getIRODSFileFactory(irodsAccount); + IRODSFile irodsFile = irodsFileFactory.instanceIRODSFile(path); + ticketClientOperations.getOperationFromIRODSUsingTicket(ticketString, irodsFile, tempDir, null, null); + + String filename = path.substring(path.lastIndexOf("/") + 1, path.length()); + File obj = findFileInDirectory(tempDir, filename); + + if (obj == null) { + throw new DataGridTicketDownloadException("File not found", path, ticketString); + } + + file = obj; + + if (obj.isDirectory()) { + file = zipService.createZip(tempDir, obj); + } + } catch (InvalidUserException e) { + logger.error("Invalid user. Cannot download files as anonymous."); + throw new DataGridTicketInvalidUserException("Invalid user anonymous"); + } catch (FileNotFoundException e) { + logger.error("Could not get file using ticket. File not found: {}", e); + throw new DataGridTicketDownloadException("File not found", path, ticketString); + } catch (JargonException e) { + logger.error("Get file using a ticket caused an error: {}", e); + int code = e.getUnderlyingIRODSExceptionCode(); + + String msg = "Download failed"; + if (ticketErroCodeMap.containsKey(code)) { + msg = ticketErroCodeMap.get(code); + } + + throw new DataGridTicketDownloadException(msg, path, ticketString); + } finally { + closeAccess(); + } + + return file; + } + + @Override + public void deleteTempTicketDir() { + FileUtils.deleteQuietly(new File(TEMP_TICKET_DIR)); + } + + /** + * Finds a file/directory within another directory + * @param directory directory to look for files + * @param filename file where are looking for + * @return File representing the file found within the given directory + */ + private File findFileInDirectory(File directory, String filename) { + File[] files = directory.listFiles((dir, name) -> name.equals(filename)); + + if (files == null || files.length == 0) return null; + + return files[0]; + } + + /** + * Sets up all necessary stuff for an anonymous user to be able to interact with the grid. This interaction means + * iput & iget. + */ + private void setUpAccess() { + try { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if(authentication == null || !(authentication.getDetails() instanceof UserTokenDetails)) { + logger.warn("Accessing files and collections with tickets using anonymous account"); + irodsAccount = IRODSAccount.instanceForAnonymous(host, port, ANONYMOUS_HOME_DIRECTORY, zone, RESOURCE); + } else { + logger.warn("Accessing files and collections with tickets as authenticated user"); + irodsAccount = ((UserTokenDetails) authentication.getDetails()).getIrodsAccount(); + } + + TicketServiceFactory ticketServiceFactory = new TicketServiceFactoryImpl(irodsAccessObjectFactory); + ticketClientOperations = ticketServiceFactory.instanceTicketClientOperations(irodsAccount); + } catch (JargonException e) { + logger.error("Could not set up anonymous access"); + } + } + + /** + * Close connection of whoever has access to iRODS using this service. + */ + private void closeAccess() { + if (irodsAccessObjectFactory == null) { + return; + } + + logger.info("Destroying ticket client access"); + irodsAccessObjectFactory.closeSessionAndEatExceptions(); + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TicketServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TicketServiceImpl.java new file mode 100755 index 000000000..87619baa4 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/TicketServiceImpl.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketNotFoundException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.DataNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.ticket.Ticket; +import org.irods.jargon.ticket.TicketAdminService; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Transactional +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES) +public class TicketServiceImpl implements TicketService { + + private static final Logger logger = LoggerFactory.getLogger(TicketServiceImpl.class); + private static final int OFFSET = 0; + private static final String GRID_FILE_SEPARATOR = "/"; + + @Autowired + private IRODSServices irodsServices; + + + @Override + public List findAll() throws DataGridConnectionRefusedException { + logger.info("Find all tickets"); + + TicketAdminService tas = irodsServices.getTicketAdminService(); + List dgTickets; + List tickets; + + try { + tickets = tas.listAllTickets(OFFSET); + } catch (JargonException e) { + logger.info("Could not list all tickets in the grid: {}.", e.getMessage()); + tickets = new ArrayList<>(); + } + + dgTickets = convertListOfTickets(tickets); + + return dgTickets; + } + + @Override + public boolean delete(String ticketString) throws DataGridConnectionRefusedException { + if(ticketString == null || ticketString.isEmpty()) { + logger.error("Could not delete ticket: Ticket ID null or emtpy"); + return false; + } + + logger.info("Deleting ticket {}", ticketString); + + boolean ticketDeleted = false; + + TicketAdminService tas = irodsServices.getTicketAdminService(); + + try { + ticketDeleted = tas.deleteTicket(ticketString); + } catch (JargonException e) { + logger.info("Could not delete ticket {}: {}.", ticketString, e.getMessage()); + } + + return ticketDeleted; + } + + @Override + public boolean bulkDelete(List ticketStrings) throws DataGridConnectionRefusedException { + logger.info("Delete list of tickets"); + + if (ticketStrings == null || ticketStrings.isEmpty()) { + logger.error("Could not bulk delete tickets: Null or empty list provided."); + return false; + } + + boolean ticketsDeleted = true; + + for (String ts: ticketStrings) { + ticketsDeleted &= delete(ts); + } + + return ticketsDeleted; + } + + @Override + public String create(DataGridTicket dgTicket) throws DataGridConnectionRefusedException, DataGridTicketException { + logger.info("Create ticket"); + + if(dgTicket == null) { + logger.info("Could not create ticket: Null ticket provided."); + throw new DataGridTicketException("Could not create ticket: null ticket provided."); + } + + if(dgTicket.getPath().isEmpty()) { + logger.info("Could not create ticket: Ticket with no path."); + throw new DataGridTicketException("Could not create ticket: path is empty"); + } + + TicketCreateModeEnum ticketType = TicketCreateModeEnum.UNKNOWN; + if(dgTicket.getType() == DataGridTicket.TicketType.READ) ticketType = TicketCreateModeEnum.READ; + if(dgTicket.getType() == DataGridTicket.TicketType.WRITE) ticketType = TicketCreateModeEnum.WRITE; + + String path = dgTicket.getPath(); + int idxOfSeparator = path.lastIndexOf(GRID_FILE_SEPARATOR); + String parentPath = path.substring(0, idxOfSeparator); + String item = path.substring(idxOfSeparator + 1, path.length()); + + String ticketString = ""; + + try { + IRODSFile irodsFile = irodsServices.getIRODSFileFactory().instanceIRODSFile(parentPath, item); + TicketAdminService tas = irodsServices.getTicketAdminService(); + ticketString = tas.createTicket(ticketType, irodsFile, dgTicket.getTicketString()); + dgTicket.setTicketString(ticketString); // set ticket string created by the grid + + modify(dgTicket); + } catch (JargonException e) { + logger.error("Could not create a ticket: {}", e); + throw new DataGridTicketException(e.getMessage()); + } + + return ticketString; + } + + @Override + public DataGridTicket find(String ticketId) throws DataGridConnectionRefusedException, + DataGridTicketNotFoundException { + logger.info("Find ticket {}", ticketId); + + DataGridTicket dgTicket = null; + + TicketAdminService tas = irodsServices.getTicketAdminService(); + + try { + Ticket t = tas.getTicketForSpecifiedTicketString(ticketId); + dgTicket = convertTicketToDataGridTicket(t); + + dgTicket.setHosts(tas.listAllHostRestrictionsForSpecifiedTicket(ticketId, OFFSET)); + dgTicket.setUsers(tas.listAllUserRestrictionsForSpecifiedTicket(ticketId, OFFSET)); + dgTicket.setGroups(tas.listAllGroupRestrictionsForSpecifiedTicket(ticketId, OFFSET)); + } catch (DataNotFoundException e) { + throw new DataGridTicketNotFoundException("Ticket does not exist"); + } catch (JargonException e) { + logger.error("Could not find ticket with string: {}", ticketId); + } + + return dgTicket; + } + + @Override + public DataGridTicket modify(DataGridTicket t) throws DataGridConnectionRefusedException, DataGridTicketException { + logger.info("Modify ticket"); + + if(t == null) { + logger.error("Null ticket provided."); + throw new DataGridTicketException("Null ticket instance"); + } + + if(t.getTicketString().isEmpty()) { + logger.error("Ticket with empty string provided."); + throw new DataGridTicketException("Ticket string missing"); + } + + String ticketString = t.getTicketString(); + + DataGridTicket dgTicket; + try { + updateHostRestrictions(t); + updateUserRestrictions(t); + updateGroupRestrictions(t); + + TicketAdminService tas = irodsServices.getTicketAdminService(); + Ticket ticketUpdated = tas.compareGivenTicketToActualAndUpdateAsNeeded(convertDataGridTicketToTicket(t)); + dgTicket = convertTicketToDataGridTicket(ticketUpdated); + + dgTicket.setHosts(tas.listAllHostRestrictionsForSpecifiedTicket(ticketString, OFFSET)); + dgTicket.setUsers(tas.listAllUserRestrictionsForSpecifiedTicket(ticketString, OFFSET)); + dgTicket.setGroups(tas.listAllGroupRestrictionsForSpecifiedTicket(ticketString, OFFSET)); + } catch (JargonException e) { + logger.error("Could not modify ticket"); + throw new DataGridTicketException(e.getMessage()); + } + + return dgTicket; + } + + private void updateHostRestrictions(DataGridTicket t) throws JargonException, + DataGridConnectionRefusedException { + logger.info("Update host restrictions for ticket {}", t.getTicketString()); + String ticketString = t.getTicketString(); + TicketAdminService tas = irodsServices.getTicketAdminService(); + List currHosts = tas.listAllHostRestrictionsForSpecifiedTicket(ticketString, OFFSET); + + for(String host: t.getHosts()) { + if(!host.isEmpty() && !currHosts.contains(host)) { + tas.addTicketHostRestriction(ticketString, host); + } + } + + for(String host: currHosts) { + if(!t.getHosts().contains(host)) tas.removeTicketHostRestriction(ticketString, host); + } + } + + private void updateUserRestrictions(DataGridTicket t) throws JargonException, + DataGridConnectionRefusedException { + logger.info("Update user restrictions for ticket {}", t.getTicketString()); + String ticketString = t.getTicketString(); + TicketAdminService tas = irodsServices.getTicketAdminService(); + List currUsers = tas.listAllUserRestrictionsForSpecifiedTicket(ticketString, OFFSET); + + for(String user: t.getUsers()) { + if(!user.isEmpty() && !currUsers.contains(user)) { + tas.addTicketUserRestriction(ticketString, user); + } + } + + for(String user: currUsers) { + if(!t.getUsers().contains(user)) tas.removeTicketUserRestriction(ticketString, user); + } + } + + private void updateGroupRestrictions(DataGridTicket t) throws JargonException, + DataGridConnectionRefusedException { + logger.info("Update group restrictions for ticket {}", t.getTicketString()); + String ticketString = t.getTicketString(); + TicketAdminService tas = irodsServices.getTicketAdminService(); + List currGroups = tas.listAllGroupRestrictionsForSpecifiedTicket(ticketString, OFFSET); + + for(String group: t.getGroups()) { + if(!group.isEmpty() && !currGroups.contains(group)) { + tas.addTicketGroupRestriction(ticketString, group); + } + } + + for(String group: currGroups) { + if(!t.getGroups().contains(group)) tas.removeTicketGroupRestriction(ticketString, group); + } + } + + private List convertListOfTickets(List tickets) { + List dgTickets = new ArrayList<>(); + + for(Ticket t: tickets) { + dgTickets.add(convertTicketToDataGridTicket(t)); + } + + return dgTickets; + } + + private DataGridTicket convertTicketToDataGridTicket(Ticket t) { + DataGridTicket dgTicket = new DataGridTicket(); + + dgTicket.setTicketString(t.getTicketString()); + dgTicket.setOwner(t.getOwnerName()); + dgTicket.setPath(t.getIrodsAbsolutePath()); + dgTicket.setTicketString(t.getTicketString()); + dgTicket.setExpirationDate(t.getExpireTime()); + dgTicket.setUsesLimit(t.getUsesLimit()); + dgTicket.setUsesCount(t.getUsesCount()); + dgTicket.setWriteByteLimit(t.getWriteByteLimit()); + dgTicket.setWriteByteCount(t.getWriteByteCount()); + dgTicket.setWriteFileLimit(t.getWriteFileLimit()); + dgTicket.setWriteFileCount(t.getWriteFileCount()); + + DataGridTicket.TicketType dgTicketType; + + if(t.getType() == TicketCreateModeEnum.READ){ + dgTicketType = DataGridTicket.TicketType.READ; + } + else if (t.getType() == TicketCreateModeEnum.WRITE) { + dgTicketType = DataGridTicket.TicketType.WRITE; + } + else { + dgTicketType = DataGridTicket.TicketType.UNKNOWN; + } + + dgTicket.setType(dgTicketType); + + if(t.getObjectType() == Ticket.TicketObjectType.COLLECTION) + dgTicket.setIsCollection(true); + + return dgTicket; + } + + private Ticket convertDataGridTicketToTicket(DataGridTicket dgTicket) { + Ticket ticket = new Ticket(); + + ticket.setTicketString(dgTicket.getTicketString()); + ticket.setOwnerName(dgTicket.getOwner()); + ticket.setIrodsAbsolutePath(dgTicket.getPath()); + ticket.setTicketString(dgTicket.getTicketString()); + ticket.setExpireTime(dgTicket.getExpirationDate()); + ticket.setUsesLimit(dgTicket.getUsesLimit()); + ticket.setUsesCount(dgTicket.getUsesCount()); + ticket.setWriteByteLimit(dgTicket.getWriteByteLimit()); + ticket.setWriteByteCount(dgTicket.getWriteByteCount()); + ticket.setWriteFileLimit(dgTicket.getWriteFileLimit()); + ticket.setWriteFileCount(dgTicket.getWriteFileCount()); + + TicketCreateModeEnum ticketMode; + + if(dgTicket.getType() == DataGridTicket.TicketType.READ) ticketMode = TicketCreateModeEnum.READ; + else if (dgTicket.getType() == DataGridTicket.TicketType.WRITE) ticketMode = TicketCreateModeEnum.WRITE; + else ticketMode = TicketCreateModeEnum.UNKNOWN; + + ticket.setType(ticketMode); + + if(dgTicket.isCollection()) ticket.setObjectType(Ticket.TicketObjectType.COLLECTION); + + return ticket; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UploadServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UploadServiceImpl.java new file mode 100755 index 000000000..fc542c17e --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UploadServiceImpl.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; + +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.ResourceAO; +import org.irods.jargon.core.pub.Stream2StreamAO; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridFileAlreadyExistsException; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.ResourceService; +import com.emc.metalnx.services.interfaces.RuleService; +import com.emc.metalnx.services.interfaces.UploadService; +import com.emc.metalnx.services.machine.util.DataGridUtils; + +@Service +@Transactional +public class UploadServiceImpl implements UploadService { + + private static final int BUFFER_SIZE = 4 * 1024 * 1024; + private static final Logger logger = LoggerFactory.getLogger(UploadServiceImpl.class); + + @Autowired + private RuleService rs; + + @Autowired + private FileOperationService fos; + + @Autowired + private IRODSServices is; + + @Autowired + private ResourceService resourceService; + + @Override + public boolean upload(MultipartFile file, String targetPath, boolean computeCheckSum, boolean replicateFile, + String replicationResc, String destResc, boolean overwrite) throws DataGridException { + + if (file == null || file.isEmpty() || "".equals(targetPath) || targetPath == null || "".equals(destResc) + || destResc == null) { + logger.error("File could not be sent to the data grid."); + return false; + } + + InputStream inputStream; + try { + inputStream = file.getInputStream(); + } catch (IOException e) { + logger.error("Could not get input stream from file: ", e.getMessage()); + throw new DataGridException("Could not get input stream from file."); + } + + String defaultStorageResource = is.getDefaultStorageResource(); + + logger.info("Setting default resource to {}", destResc); + + // Setting temporarily the defaultStorageResource for the logged user + is.setDefaultStorageResource(destResc); + + boolean isFileUploaded; + + // Getting DataObjectAO in order to create the new file + IRODSFileFactory irodsFileFactory = is.getIRODSFileFactory(); + Stream2StreamAO stream2StreamA0 = is.getStream2StreamAO(); + IRODSFile targetFile = null; + + try { + String fileName = file.getOriginalFilename(); + + if (fileName.isEmpty()) + fileName = file.getName(); + + targetFile = irodsFileFactory.instanceIRODSFile(targetPath, fileName); + + // file already exists and we do not want to overwrite it, the transferring is + // aborted. + if (targetFile.exists() && !overwrite) { + String msg = "File already exists. Not overwriting it."; + logger.info(msg); + throw new DataGridFileAlreadyExistsException(msg); + } + + // Transfering file to iRODS filesystem + stream2StreamA0.transferStreamToFileUsingIOStreams(inputStream, (File) targetFile, 0, BUFFER_SIZE); + + // Computing a check sum for this file just uploaded to iRODS + if (computeCheckSum) + fos.computeChecksum(targetPath, fileName); + + // Replicating file into desired resource + if (replicateFile) + fos.replicateDataObject(targetFile.getPath(), replicationResc, false); + + // Getting list of resources for upload + HashMap resourceMap = null; + try { + ResourceAO resourceAO = is.getResourceAO(); + resourceMap = DataGridUtils.buildMapForResourcesNamesAndMountPoints(resourceAO.findAll()); + } catch (JargonException e) { + logger.error("Could not build Resource map for upload", e); + throw new DataGridException("Procedures not run after upload. Resource Map creation failed."); + } + + String objPath = targetFile.getCanonicalPath(); + String filePath = resourceMap.get(destResc) + objPath.substring(objPath.indexOf("/", 1), objPath.length()); + + DataGridResource dgDestResc = resourceService.find(destResc); + String host = dgDestResc.getHost(); + + rs.execBamCramMetadataRule(host, objPath, filePath); + rs.execVCFMetadataRule(host, objPath, filePath); + rs.execPopulateMetadataRule(host, objPath); + rs.execImageRule(host, objPath, filePath); + rs.execIlluminaMetadataRule(dgDestResc, targetPath, objPath); + rs.execManifestFileRule(host, targetPath, objPath, filePath); + + isFileUploaded = true; + } catch (JargonException e) { + fos.deleteDataObject(targetFile.getPath(), true); + logger.error("Upload stream failed from Metalnx to the data grid. {}", e.getMessage()); + throw new DataGridException("Upload failed. Resource(s) might be full."); + } catch (IOException e) { + logger.error("Could not get canonical path", e.getMessage()); + throw new DataGridException("Could not get canonical path"); + } finally { + try { + inputStream.close(); // Closing streams opened + } catch (IOException e) { + logger.error("Could close stream: ", e.getMessage()); + } + } + + // Setting the default resource back to the original one. + is.setDefaultStorageResource(defaultStorageResource); + + return isFileUploaded; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UserBookmarkServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UserBookmarkServiceImpl.java new file mode 100755 index 000000000..143f16f05 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UserBookmarkServiceImpl.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.dao.UserBookmarkDao; +import com.emc.metalnx.core.domain.dao.UserDao; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserBookmark; +import com.emc.metalnx.services.interfaces.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +@Service +@Transactional +public class UserBookmarkServiceImpl implements UserBookmarkService { + + @Autowired + UserDao userDao; + + @Autowired + UserBookmarkDao userBookmarkDao; + + @Autowired + IRODSServices irodsServices; + + @Autowired + AdminServices adminServices; + + @Autowired + UserService userService; + + @Autowired + CollectionService collectionService; + + private static final Logger logger = LoggerFactory.getLogger(UserBookmarkServiceImpl.class); + + @Override + public boolean updateBookmarks(DataGridUser user, Set toAdd, Set toRemove) { + + boolean operationResult = true; + + try { + if (toAdd != null) { + for (String path : toAdd) { + if (!findBookmarksForUserAsString(user).contains(path)) { + userBookmarkDao.addByUserAndPath(user, path, collectionService.isCollection(path)); + } + } + } + + if (toRemove != null) { + for (String path : toRemove) { + userBookmarkDao.removeByUserAndPath(user, path); + } + } + } + catch (Exception e) { + operationResult = false; + logger.error("Could not modify user bookmark for {}", user.getUsername(), e); + } + + return operationResult; + } + + @Override + public List findBookmarksForUserAsString(DataGridUser user) { + List bookmarks = userBookmarkDao.findByUser(user); + List strings = new ArrayList(); + + for (DataGridUserBookmark bookmark : bookmarks) { + strings.add(bookmark.getPath()); + } + + return strings; + } + + @Override + public List findBookmarksOnPath(String path) { + return userBookmarkDao.findBookmarksByPath(path); + } + + @Override + public boolean removeBookmarkBasedOnPath(String path) { + return userBookmarkDao.removeByPath(path); + } + + @Override + public boolean removeBookmarkBasedOnRelativePath(String path) { + return userBookmarkDao.removeByParentPath(path); + } + + @Override + public List findBookmarksPaginated(DataGridUser user, int start, int length, String searchString, String orderBy, + String orderDir, boolean onlyCollections) { + return userBookmarkDao.findByUserPaginated(user, start, length, searchString, orderBy, orderDir, onlyCollections); + } + + @Override + public List findBookmarksPaginated(DataGridUser user, int start, int length, String searchString, List orderBy, + List orderDir, boolean onlyCollections) { + return userBookmarkDao.findByUserPaginated(user, start, length, searchString, orderBy, orderDir, onlyCollections); + } + + @Override + public boolean removeBookmarkBasedOnUser(DataGridUser user) { + return userBookmarkDao.removeByUser(user); + } + + @Override + public boolean updateBookmark(String oldPath, String newPath) { + return userBookmarkDao.updateBookmark(oldPath, newPath); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UserProfileServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UserProfileServiceImpl.java new file mode 100755 index 000000000..e15e4d44d --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UserProfileServiceImpl.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.dao.UserDao; +import com.emc.metalnx.core.domain.dao.UserProfileDao; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.UserProfile; +import com.emc.metalnx.services.interfaces.UserProfileService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +public class UserProfileServiceImpl implements UserProfileService { + + @Autowired + UserDao userDao; + + @Autowired + UserProfileDao userProfileDao; + + @Override + public List findAll() { + return userProfileDao.findAll(UserProfile.class); + } + + @Override + public List findByQueryString(String query) { + return userProfileDao.findByQueryString(query); + } + + @Override + public UserProfile findById(Long id) { + return userProfileDao.findByID(UserProfile.class, id); + } + + @Override + public void modifyUserProfile(UserProfile profile) { + userProfileDao.merge(profile); + } + + @Override + public Long createUserProfile(UserProfile userProfile) { + return userProfileDao.save(userProfile); + } + + @Override + public void removeUserProfile(UserProfile profile) { + profile = userProfileDao.findByID(UserProfile.class, profile.getProfileId()); + for (DataGridUser user : profile.getUsers()) { + DataGridUser userToUpdate = userDao.findByID(DataGridUser.class, user.getId()); + userToUpdate.setUserProfile(null); + userDao.merge(userToUpdate); + } + profile.getUsers().clear(); + userProfileDao.delete(profile); + } + + @Override + public int countAll() { + return userProfileDao.findAll(UserProfile.class).size(); + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UserServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UserServiceImpl.java new file mode 100755 index 000000000..060f46507 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/UserServiceImpl.java @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.dao.UserDao; +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.UserProfile; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.*; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.protovalues.UserTypeEnum; +import org.irods.jargon.core.pub.CollectionAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.UserAO; +import org.irods.jargon.core.pub.UserGroupAO; +import org.irods.jargon.core.pub.domain.User; +import org.irods.jargon.core.pub.domain.UserGroup; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +@Service +@Transactional +public class UserServiceImpl implements UserService { + + @Autowired + UserDao userDao; + + @Autowired + GroupService groupService; + + @Autowired + UserBookmarkService userBookmarkService; + + @Autowired + FavoritesService favoritesService; + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private ConfigService configService; + + private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); + + @Override + public List findAll() { + List users = userDao.findAll(DataGridUser.class); + Collections.sort(users); + return users; + } + + @Override + public boolean createUser(DataGridUser user, String password) throws JargonException, DataGridConnectionRefusedException { + + UserAO userAO = irodsServices.getUserAO(); + + // Translating to iRODS model format + User irodsUser = new User(); + irodsUser.setName(user.getUsername()); + irodsUser.setZone(user.getAdditionalInfo()); + + if (user.getUserType().compareTo(UserTypeEnum.RODS_ADMIN.getTextValue()) == 0) { + irodsUser.setUserType(UserTypeEnum.RODS_ADMIN); + } + else { + irodsUser.setUserType(UserTypeEnum.RODS_USER); + } + + // Creating user + irodsUser = userAO.addUser(irodsUser); + + user.setDataGridId(Long.parseLong(irodsUser.getId())); + user.setEnabled(true); + userDao.save(user); + + // Setting password + userAO.changeAUserPasswordByAnAdmin(user.getUsername(), password); + + return true; + + } + + @Override + public boolean deleteUserByUsername(String username) throws DataGridConnectionRefusedException { + + UserAO userAO = irodsServices.getUserAO(); + + try { + + DataGridUser user = findByUsername(username).get(0); + String userHomeFolder = String.format("/%s/home/%s", configService.getIrodsZone(), username); + + // Removing user + userAO.deleteUser(username); + userDao.deleteByUsername(username); + + // Removing favorites and user bookmarks before removing user + userBookmarkService.removeBookmarkBasedOnUser(user); + userBookmarkService.removeBookmarkBasedOnPath(userHomeFolder); + + favoritesService.removeFavoriteBasedOnUser(user); + favoritesService.removeFavoriteBasedOnPath(userHomeFolder); + + return true; + } + catch (Exception e) { + logger.error("Could not delete user with username [" + username + "]"); + } + return false; + } + + @Override + public boolean modifyUser(DataGridUser modifyUser) throws DataGridConnectionRefusedException { + + UserAO userAO = irodsServices.getUserAO(); + + try { + + User iRodsUser = userAO.findById(String.valueOf(modifyUser.getDataGridId())); + + boolean iRodsFieldsModified = false; + + // check which fields were modified (iRODS) + if (iRodsUser.getZone().compareTo(modifyUser.getAdditionalInfo()) != 0) { + iRodsUser.setZone(modifyUser.getAdditionalInfo()); + iRodsFieldsModified = true; + } + + if (!iRodsUser.getUserType().getTextValue().equals(modifyUser.getUserType())) { + if (modifyUser.getUserType().compareTo(UserTypeEnum.RODS_ADMIN.getTextValue()) == 0) { + iRodsUser.setUserType(UserTypeEnum.RODS_ADMIN); + } + else { + iRodsUser.setUserType(UserTypeEnum.RODS_USER); + } + + iRodsFieldsModified = true; + } + + // updating user in iRODS if any field was modified + if (iRodsFieldsModified) { + userAO.updateUser(iRodsUser); + } + + DataGridUser applicationUser = userDao.findByUsernameAndZone(modifyUser.getUsername(), modifyUser.getAdditionalInfo()); + + // check which fields were modified (our database) + if (applicationUser.getAdditionalInfo() == null || applicationUser.getAdditionalInfo().compareTo(modifyUser.getAdditionalInfo()) != 0) { + applicationUser.setAdditionalInfo(modifyUser.getAdditionalInfo()); + } + + if (applicationUser.getFirstName() == null || applicationUser.getFirstName().compareTo(modifyUser.getFirstName()) != 0) { + applicationUser.setFirstName(modifyUser.getFirstName()); + } + + if (applicationUser.getLastName() == null || applicationUser.getLastName().compareTo(modifyUser.getLastName()) != 0) { + applicationUser.setLastName(modifyUser.getLastName()); + } + + if (applicationUser.getEmail() == null || applicationUser.getEmail().compareTo(modifyUser.getEmail()) != 0) { + applicationUser.setEmail(modifyUser.getEmail()); + } + + if (applicationUser.getCompany() == null || applicationUser.getCompany().compareTo(modifyUser.getCompany()) != 0) { + applicationUser.setCompany(modifyUser.getCompany()); + } + + if (applicationUser.getDepartment() == null || applicationUser.getDepartment().compareTo(modifyUser.getDepartment()) != 0) { + applicationUser.setDepartment(modifyUser.getDepartment()); + } + + applicationUser.setUserProfile(modifyUser.getUserProfile()); + applicationUser.setLocale(modifyUser.getLocale()); + applicationUser.setOrganizationalRole(modifyUser.getOrganizationalRole()); + applicationUser.setUserType(modifyUser.getUserType()); + applicationUser.setForceFileOverwriting(modifyUser.isForceFileOverwriting()); + userDao.merge(applicationUser); + + // Changing password if a new password is set + String newPassword = modifyUser.getPassword(); + if (newPassword != null && !newPassword.isEmpty()) { + userAO.changeAUserPasswordByAnAdmin(modifyUser.getUsername(), newPassword); + } + + return true; + } + catch (JargonException e) { + e.printStackTrace(); + } + return false; + } + + @Override + public List findByDataGridIds(String[] ids) { + List users = userDao.findByDataGridIdList(ids); + Collections.sort(users); + return users; + } + + @Override + public List findByUsername(String username) { + List users = userDao.findByUsername(username); + Collections.sort(users); + return users; + } + + @Override + public DataGridUser findByUsernameAndAdditionalInfo(String username, String additionalInfo) { + return userDao.findByUsernameAndZone(username, additionalInfo); + } + + @Override + public int countAll() { + return userDao.findAll(DataGridUser.class).size(); + } + + @Override + public List findByQueryString(String query) { + List users = userDao.findByQueryString(query); + Collections.sort(users); + return users; + } + + @Override + public String[] getGroupIdsForUser(DataGridUser user) throws DataGridConnectionRefusedException { + UserGroupAO userGroupAO = irodsServices.getGroupAO(); + + try { + // Getting list of groups the user belongs to. + List groups = userGroupAO.findUserGroupsForUser(user.getUsername()); + + // Building Data Grid IDs list + String[] ids = new String[groups.size()]; + for (int i = 0; i < groups.size(); i++) { + ids[i] = groups.get(i).getUserGroupId(); + } + + // Returning results + return ids; + } + catch (JargonException e) { + logger.error("Could not find group list for user [" + user.getUsername() + "], ", e); + } + + // If something goes wrong, return empty list. + return new String[0]; + } + + @Override + public String[] getGroupIdsForUser(String username, String additionalInfo) throws DataGridConnectionRefusedException { + + DataGridUser user = findByUsernameAndAdditionalInfo(username, additionalInfo); + + if (user == null) { + return new String[0]; + } + + return getGroupIdsForUser(user); + } + + @Override + public boolean updateGroupList(DataGridUser user, List groups) throws DataGridConnectionRefusedException { + + UserGroupAO groupAO = irodsServices.getGroupAO(); + + try { + + // List current groups for user + List groupsFromIrods = groupAO.findUserGroupsForUser(user.getUsername()); + + // Building set with iRODS IDs already on this group + HashMap idsFromIrods = new HashMap(); + for (UserGroup groupFromIrods : groupsFromIrods) { + idsFromIrods.put(Long.valueOf(groupFromIrods.getUserGroupId()), groupFromIrods); + } + + // Building set with iRODS IDs coming from UI + HashMap idsFromUi = new HashMap(); + for (DataGridGroup groupFromUi : groups) { + idsFromUi.put(groupFromUi.getDataGridId(), groupFromUi); + } + + // Resolving differences from UI to iRODS + Set keysFromUi = idsFromUi.keySet(); + Set keysFromIrods = idsFromIrods.keySet(); + + // Committing changes to iRODS + for (Long dataGridId : keysFromUi) { + if (!keysFromIrods.contains(dataGridId)) { + groupService.attachUserToGroup(user, idsFromUi.get(dataGridId)); + } + } + + for (Long dataGridId : keysFromIrods) { + if (!keysFromUi.contains(dataGridId)) { + DataGridGroup group = new DataGridGroup(); + group.setGroupname(idsFromIrods.get(dataGridId).getUserGroupName()); + + if (group.getGroupname().compareTo("public") != 0) { + groupService.removeUserFromGroup(user, group); + } + } + } + + return true; + } + catch (Exception e) { + logger.info("Could not update [" + user.getUsername() + "] group list: ", e); + } + return false; + } + + @Override + public boolean applyProfileToUser(UserProfile profile, DataGridUser user) { + Set profileGroups = profile.getGroups(); + for (DataGridGroup dataGridGroup : profileGroups) { + try { + groupService.attachUserToGroup(user, dataGridGroup); + } + catch (Exception e) { + logger.info("iCAT already contain the user [" + user.getUsername() + "] on group [" + dataGridGroup.getGroupname() + "] :", e); + } + } + return true; + } + + @Override + public boolean updateReadPermissions(DataGridUser user, Map addCollectionsToRead, Map removeCollectionsToRead) + throws DataGridConnectionRefusedException { + + CollectionAO collectionAO = null; + DataObjectAO dataObjectAO = null; + IRODSFile irodsFile = null; + IRODSFileFactory irodsFileFactory = null; + boolean readPermissionsUpdated = false; + + try { + collectionAO = irodsServices.getCollectionAO(); + dataObjectAO = irodsServices.getDataObjectAO(); + irodsFileFactory = irodsServices.getIRODSFileFactory(); + + for (String path : addCollectionsToRead.keySet()) { + irodsFile = irodsFileFactory.instanceIRODSFile(path); + if (irodsFile.isDirectory()) { + // applying read permissions on a collection (not recursively) + collectionAO.setAccessPermissionReadAsAdmin(user.getAdditionalInfo(), path, user.getUsername(), addCollectionsToRead.get(path)); + } + else { + // applying read permissions on a data object + dataObjectAO.setAccessPermissionReadInAdminMode(user.getAdditionalInfo(), path, user.getUsername()); + } + } + removeAccessPermissionForUserAsAdmin(user, removeCollectionsToRead); + readPermissionsUpdated = true; + } + catch (JargonException e) { + logger.error("Could not set read permission:", e); + } + + return readPermissionsUpdated; + } + + @Override + public boolean updateWritePermissions(DataGridUser user, Map addCollectionsToWrite, Map removeCollectionsToWrite) + throws DataGridConnectionRefusedException { + + CollectionAO collectionAO = null; + DataObjectAO dataObjectAO = null; + IRODSFile irodsFile = null; + IRODSFileFactory irodsFileFactory = null; + boolean writePermissionsUpdated = false; + + try { + collectionAO = irodsServices.getCollectionAO(); + dataObjectAO = irodsServices.getDataObjectAO(); + irodsFileFactory = irodsServices.getIRODSFileFactory(); + + for (String path : addCollectionsToWrite.keySet()) { + irodsFile = irodsFileFactory.instanceIRODSFile(path); + if (irodsFile.isDirectory()) { + collectionAO.setAccessPermissionWriteAsAdmin(user.getAdditionalInfo(), path, user.getUsername(), addCollectionsToWrite.get(path)); + } + else { + dataObjectAO.setAccessPermissionWriteInAdminMode(user.getAdditionalInfo(), path, user.getUsername()); + } + } + + removeAccessPermissionForUserAsAdmin(user, removeCollectionsToWrite); + writePermissionsUpdated = true; + } + catch (JargonException e) { + logger.error("Could not set write permission:", e); + } + + return writePermissionsUpdated; + } + + @Override + public boolean updateOwnership(DataGridUser user, Map addCollectionsToOwn, Map removeCollectionsToOwn) + throws DataGridConnectionRefusedException { + + CollectionAO collectionAO = null; + DataObjectAO dataObjectAO = null; + IRODSFile irodsFile = null; + IRODSFileFactory irodsFileFactory = null; + boolean ownPermissionsUpdated = false; + + try { + collectionAO = irodsServices.getCollectionAO(); + dataObjectAO = irodsServices.getDataObjectAO(); + irodsFileFactory = irodsServices.getIRODSFileFactory(); + + for (String path : addCollectionsToOwn.keySet()) { + irodsFile = irodsFileFactory.instanceIRODSFile(path); + if (irodsFile.isDirectory()) { + collectionAO.setAccessPermissionOwnAsAdmin(user.getAdditionalInfo(), path, user.getUsername(), addCollectionsToOwn.get(path)); + } + else { + dataObjectAO.setAccessPermissionOwnInAdminMode(user.getAdditionalInfo(), path, user.getUsername()); + } + } + + removeAccessPermissionForUserAsAdmin(user, removeCollectionsToOwn); + ownPermissionsUpdated = true; + } + catch (JargonException e) { + logger.error("Could not set ownership permission:", e); + } + + return ownPermissionsUpdated; + } + + @Override + public List listUserTypes() { + List userTypes = new ArrayList(); + + userTypes.add(UserTypeEnum.RODS_ADMIN.getTextValue()); + userTypes.add(UserTypeEnum.RODS_USER.getTextValue()); + + return userTypes; + } + + @Override + public void removeAccessPermissionForUserAsAdmin(DataGridUser user, Map paths) throws JargonException, + DataGridConnectionRefusedException { + + if (paths == null || paths.isEmpty()) { + return; + } + + IRODSFileFactory irodsFileFactory = irodsServices.getIRODSFileFactory(); + CollectionAO collectionAO = irodsServices.getCollectionAO(); + DataObjectAO dataObjectAO = irodsServices.getDataObjectAO(); + IRODSFile irodsFile = null; + + for (String path : paths.keySet()) { + irodsFile = irodsFileFactory.instanceIRODSFile(path); + if (irodsFile.isDirectory()) { + collectionAO.removeAccessPermissionForUserAsAdmin(user.getAdditionalInfo(), path, user.getUsername(), paths.get(path)); + } + else { + dataObjectAO.removeAccessPermissionsForUserInAdminMode(user.getAdditionalInfo(), path, user.getUsername()); + } + } + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/ZoneServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/ZoneServiceImpl.java new file mode 100755 index 000000000..9a5c7362c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/ZoneServiceImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.irods; + +import com.emc.metalnx.core.domain.entity.DataGridZone; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.RemoteExecutionService; +import com.emc.metalnx.services.interfaces.ZoneService; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.ZoneAO; +import org.irods.jargon.core.pub.domain.Zone; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Transactional +public class ZoneServiceImpl implements ZoneService { + + @Autowired + RemoteExecutionService remoteExecutionService; + + @Autowired + private IRODSServices irodsServices; + + private static final Logger logger = LoggerFactory.getLogger(ZoneServiceImpl.class); + + @Override + public List findAll() throws DataGridConnectionRefusedException { + ZoneAO zoneAO = irodsServices.getZoneAO(); + List dataGridZones = null; + + try { + List zones = zoneAO.listZones(); + dataGridZones = new ArrayList(); + + for (Zone zone : zones) { + DataGridZone dataGridZone = new DataGridZone(); + dataGridZone.setId(Long.valueOf(zone.getZoneId())); + dataGridZone.setName(zone.getZoneName()); + dataGridZone.setType(zone.getZoneType()); + dataGridZone.setCreateTime(zone.getZoneCreateTime()); + dataGridZone.setModifyTime(zone.getZoneModifyTime()); + dataGridZone.setConnectionString(zone.getZoneConnection()); + dataGridZone.setComment(zone.getZoneComment()); + + // adding this current zone to the list + dataGridZones.add(dataGridZone); + } + } + catch (JargonException e) { + logger.info("Could not find all zones ", e); + } + + return dataGridZones; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/MiscSpecificQueryUtils.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/MiscSpecificQueryUtils.java new file mode 100644 index 000000000..215d4dc97 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/MiscSpecificQueryUtils.java @@ -0,0 +1,39 @@ +/** + * + */ +package com.emc.metalnx.services.irods.utils; + +import java.util.HashMap; +import java.util.Map; + +/** + * Misc utils for creating sql queries + * + * @author Mike Conway - NIEHS + * + */ +public class MiscSpecificQueryUtils { + + public static final Map getMapColumnsForCollections() { + Map datatableColumns = new HashMap<>(); + datatableColumns.put(0, "coll_name"); + datatableColumns.put(1, "coll_name"); + datatableColumns.put(2, "coll_owner_name"); + datatableColumns.put(4, "modify_ts"); + datatableColumns.put(5, "coll_type"); + + return datatableColumns; + } + + public static final Map getMapColumnsForDataObjects() { + Map datatableColumns = new HashMap<>(); + datatableColumns.put(0, "data_name"); + datatableColumns.put(1, "data_name"); + datatableColumns.put(2, "data_owner_name"); + datatableColumns.put(4, "modify_ts"); + datatableColumns.put(5, "data_size"); + + return datatableColumns; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/PostgresSpecificQueryProviderImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/PostgresSpecificQueryProviderImpl.java new file mode 100644 index 000000000..f2c845e30 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/PostgresSpecificQueryProviderImpl.java @@ -0,0 +1,486 @@ +/** + * + */ +package com.emc.metalnx.services.irods.utils; + +import java.util.List; + +import com.emc.metalnx.core.domain.entity.DataGridFilePropertySearch; +import com.emc.metalnx.core.domain.entity.DataGridMetadataSearch; +import com.emc.metalnx.core.domain.entity.enums.DataGridSearchOperatorEnum; +import com.emc.metalnx.core.domain.entity.enums.FilePropertyField; + +/** + * @author Mike Conway - NIEHS + * + */ +public class PostgresSpecificQueryProviderImpl implements SpecificQueryProvider { + + /** + * + */ + public PostgresSpecificQueryProviderImpl() { + } + + /* + * (non-Javadoc) + * + * @see com.emc.metalnx.services.irods.utils.SpecificQueryProvider# + * buildSpecificQueryForMetadataSearch(java.util.List, java.lang.String, + * boolean) + */ + @Override + public String buildSpecificQueryForMetadataSearch(List metadataSearch, String zone, + boolean searchAgainstColls) { + // This is the first part of the query. It selects all objects (collections or + // data objects) + // that has a piece of metadata matching the query + StringBuilder objQuery = new StringBuilder(); + + // This is the query that actually looks for metadata tags in the data grid. + StringBuilder metadataSearchQuery = new StringBuilder(); + + // This is the last part of the query. It groups the objects by their attributes + // in order to + // have unique results. + StringBuilder gb = new StringBuilder(); + + // This is the final query that will be sent to the data grid database + StringBuilder q = new StringBuilder(); + + if (searchAgainstColls) { + objQuery.append(" SELECT obj_name, parent_path, obj_owner, create_ts, modify_ts, resc_name, totalMatches"); + objQuery.append(" FROM ("); + objQuery.append(" SELECT c.coll_name as obj_name,"); + objQuery.append(" c.parent_coll_name as parent_path,"); + objQuery.append(" c.coll_owner_name as obj_owner,"); + objQuery.append(" c.create_ts as create_ts,"); + objQuery.append(" c.modify_ts as modify_ts,"); + objQuery.append(" '' as resc_name,"); + objQuery.append(" c.coll_inheritance,"); + objQuery.append(" COUNT(c.coll_name) as totalMatches"); + objQuery.append(" FROM "); + objQuery.append(" r_coll_main c "); + objQuery.append(" JOIN ( "); + + gb.append(" ) AS coll_metadata ON (c.coll_id = map_object_id) "); + gb.append(" GROUP BY "); + gb.append(" c.coll_name,"); + gb.append(" c.parent_coll_name,"); + gb.append(" c.coll_owner_name,"); + gb.append(" c.create_ts,"); + gb.append(" c.modify_ts,"); + gb.append(" resc_name,"); + gb.append(" c.coll_inheritance"); + gb.append(" ORDER BY totalMatches DESC, c.coll_name "); + gb.append(" ) AS ms "); + } else { + objQuery.append( + " SELECT obj_name, size, obj_owner, repl_num, create_ts, modify_ts, resc_name, parent_path, totalMatches"); + objQuery.append(" FROM ("); + objQuery.append(" SELECT "); + objQuery.append(" d.data_name as obj_name, "); + objQuery.append(" d.data_size as size, "); + objQuery.append(" d.data_owner_name as obj_owner, "); + objQuery.append(" d.data_repl_num as repl_num, "); + objQuery.append(" d.create_ts as create_ts,"); + objQuery.append(" d.modify_ts as modify_ts, "); + objQuery.append(" d.resc_name as resc_name, "); + objQuery.append(" c.coll_name as parent_path,"); + objQuery.append(" COUNT(d.data_name) as totalMatches"); + objQuery.append(" FROM r_data_main d "); + objQuery.append(" JOIN r_coll_main c ON (d.coll_id = c.coll_id) "); + objQuery.append(" JOIN ( "); + + gb.append(" ) AS data_obj_metadata ON (d.data_id = map_object_id) "); + gb.append(" GROUP BY "); + gb.append(" d.data_name,"); + gb.append(" d.data_size,"); + gb.append(" d.data_owner_name,"); + gb.append(" d.data_repl_num,"); + gb.append(" d.create_ts,"); + gb.append(" d.modify_ts,"); + gb.append(" d.resc_name, "); + gb.append(" c.coll_name"); + gb.append(" ORDER BY totalMatches DESC, d.data_name "); + gb.append(" ) AS ms "); + } + + for (DataGridMetadataSearch d : metadataSearch) { + metadataSearchQuery.append(d.getSpecQueryAsString()); + + // appending conditions + if (metadataSearch.indexOf(d) != metadataSearch.size() - 1) { + metadataSearchQuery.append(" UNION ALL "); + } + } + + // combining the three parts of the metadata query into a single SQL query + q.append(objQuery.toString()); + q.append(metadataSearchQuery.toString()); + q.append(gb.toString()); + + return q.toString(); + } + + /* + * (non-Javadoc) + * + * @see com.emc.metalnx.services.irods.utils.SpecificQueryProvider# + * buildWhereClauseForDataGridPropertySearch(com.emc.metalnx.core.domain.entity. + * enums.FilePropertyField, + * com.emc.metalnx.core.domain.entity.enums.DataGridSearchOperatorEnum, + * java.lang.String) + */ + @Override + public String buildWhereClauseForDataGridPropertySearch(FilePropertyField inAttribute, + DataGridSearchOperatorEnum inOperator, String inValue) { + + String whereClause = new String(); + + String attribute = inAttribute.getFieldName().replaceAll(SpecificQueryConstants.propFieldsRegex, ""); + String operator = inOperator.toString(); + String value = inValue.replaceAll(SpecificQueryConstants.regexForValue, ""); + boolean isAttributeEqualsDate = inAttribute == FilePropertyField.CREATION_DATE + || inAttribute == FilePropertyField.MODIFICATION_DATE; + + if (inOperator == DataGridSearchOperatorEnum.LIKE || inOperator == DataGridSearchOperatorEnum.NOT_LIKE) { + whereClause = String.format(" LOWER( fileProperties.%s ) %s LOWER( '%%%s%%' )", attribute, operator, value); + } else if (isAttributeEqualsDate && inOperator == DataGridSearchOperatorEnum.EQUAL) { + whereClause = String.format(" fileProperties.%s BETWEEN %s AND %d", attribute, value, + Long.parseLong(value) + 60); + } else if (isAttributeEqualsDate || inAttribute == FilePropertyField.REPLICA_NUMBER + || inAttribute == FilePropertyField.SIZE) { + whereClause = String.format(" fileProperties.%s %s %s", attribute, operator, value); + } else { + whereClause = String.format(" LOWER( fileProperties.%s ) %s LOWER( '%s' )", attribute, operator, value); + } + + return whereClause; + + } + + /* + * (non-Javadoc) + * + * @see com.emc.metalnx.services.irods.utils.SpecificQueryProvider# + * addOffsetAndLimitToQuery(java.lang.String, int, int) + */ + @Override + public String addOffsetAndLimitToQuery(String query, int offset, int limit) { + return null; + } + + /* + * (non-Javadoc) + * + * @see com.emc.metalnx.services.irods.utils.SpecificQueryProvider# + * buildQueryForCountOfItemsMatchingMetadataSearch(java.util.List, + * java.lang.String, boolean) + */ + @Override + public String buildQueryForCountOfItemsMatchingMetadataSearch(List metadataSearch, + String zone, boolean searchAgainstColls) { + StringBuilder query = new StringBuilder(); + + query.append("WITH searchMetadata AS ("); + query.append(this.buildSpecificQueryForMetadataSearch(metadataSearch, zone, searchAgainstColls)); + query.append(") "); + query.append("SELECT COUNT(*) "); + query.append("FROM searchMetadata "); + return query.toString(); + } + + /* + * (non-Javadoc) + * + * @see com.emc.metalnx.services.irods.utils.SpecificQueryProvider# + * buildQueryCountItemsMatchingPropertiesSearch(java.util.List, + * java.lang.String, boolean) + */ + @Override + public String buildQueryCountItemsMatchingPropertiesSearch(List filePropertiesSearch, + String zone, boolean searchAgainstColls) { + + StringBuilder query = new StringBuilder(); + StringBuilder selStringBuilder = new StringBuilder(); + + if (searchAgainstColls) { + selStringBuilder.append(buildSelectClauseForCountCollectionsForPropertiesSearch()); + } else { + selStringBuilder.append(buildSelectClauseForCountDataObjectsForPropertiesSearch()); + } + + query.append("SELECT COUNT(*) FROM ( "); + query.append(selStringBuilder); + query.append(" ) AS fileProperties WHERE "); + + for (int i = 0; i < filePropertiesSearch.size(); i++) { + + query.append(buildWhereClauseForDataGridPropertySearch(filePropertiesSearch.get(i).getAttribute(), + filePropertiesSearch.get(i).getOperator(), filePropertiesSearch.get(i).getValue())); + + if (i < filePropertiesSearch.size() - 1) { + query.append(" AND "); + } + } + return query.toString(); + } + + @Override + public String buildQueryForFilePropertiesSearch(List filePropertiesSearches, + String zone, boolean searchAgainstColls, int offset, int limit) { + + StringBuilder query = new StringBuilder(); + + if (filePropertiesSearches.size() > 0) { + if (searchAgainstColls) { + query.append(buildSelectClauseForCollectionsForPropertiesSearch()); + } else { + query.append(buildSelectClauseForDataObjectsForPropertiesSearch()); + } + + for (DataGridFilePropertySearch filePropertiesSearch : filePropertiesSearches) { + + // where clause - conditions + query.append(buildWhereClauseForDataGridPropertySearch(filePropertiesSearch.getAttribute(), + filePropertiesSearch.getOperator(), filePropertiesSearch.getValue())); + + // appending conditions + if (filePropertiesSearches.indexOf(filePropertiesSearch) != filePropertiesSearches.size() - 1) { + query.append(" AND "); + } + } + } + + if (offset == 0 && limit == 0) { + // ignored + } else { + query.append(" OFFSET "); + query.append(offset); + query.append(" LIMIT "); + query.append(limit); + } + + return query.toString(); + + } + + /** + * select clause for count data objects matching file properties query + * + * @return String with select + */ + private String buildSelectClauseForCountDataObjectsForPropertiesSearch() { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT r_data_main.data_name as name, r_data_main.data_repl_num as repl_num,"); + sb.append(" r_data_main.data_owner_name as owner_name, r_data_main.data_owner_zone as owner_zone, "); + sb.append(" r_data_main.data_size as size, r_data_main.resc_name, "); + sb.append( + " CASE WHEN r_coll_main.parent_coll_name = '/' THEN '/' || r_data_main.data_name ELSE r_coll_main.coll_name || '/' || r_data_main.data_name END as path, "); + sb.append(" r_data_main.data_checksum as checksum, CAST(r_data_main.create_ts AS BIGINT), "); + sb.append(" CAST(r_data_main.modify_ts AS BIGINT) FROM r_data_main INNER JOIN r_coll_main ON "); + sb.append(" r_data_main.coll_id = r_coll_main.coll_id"); + return sb.toString(); + + } + + /** + * select clause for count collections matching file properties query + * + * @return String with select + */ + private String buildSelectClauseForCountCollectionsForPropertiesSearch() { + StringBuilder sb = new StringBuilder(); + + sb.append( + "SELECT replace(r_coll_main.coll_name, r_coll_main.parent_coll_name || '/', '') AS name, 0 AS repl_num,"); + sb.append(" r_coll_main.coll_owner_name AS owner_name, r_coll_main.coll_owner_zone AS owner_zone, 0 AS size, "); + sb.append(" '' AS resc_name, r_coll_main.coll_name AS path, '' AS checksum, "); + sb.append(" CAST(r_coll_main.create_ts AS BIGINT), CAST(r_coll_main.modify_ts AS BIGINT) FROM r_coll_main "); + return sb.toString(); + } + + private String buildSelectClauseForDataObjectsForPropertiesSearch() { + StringBuilder query = new StringBuilder(); + query.append("SELECT * FROM ( "); + query.append(" SELECT"); + query.append(" r_data_main.data_name AS name,"); + query.append(" r_data_main.data_repl_num AS repl_num,"); + query.append(" r_data_main.data_owner_name AS owner_name,"); + query.append(" r_data_main.data_owner_zone AS owner_zone,"); + query.append(" r_data_main.data_size AS size,"); + query.append(" r_data_main.resc_name,"); + query.append( + " CASE WHEN r_coll_main.parent_coll_name = '/' THEN '/' || r_data_main.data_name ELSE r_coll_main.coll_name || '/' || r_data_main.data_name END AS path,"); + query.append(" r_data_main.data_checksum AS checksum,"); + query.append(" CAST(r_data_main.create_ts AS BIGINT), "); + query.append(" CAST(r_data_main.modify_ts AS BIGINT) "); + query.append(" FROM"); + query.append(" r_data_main "); + query.append(" INNER JOIN "); + query.append(" r_coll_main "); + query.append(" ON "); + query.append(" r_data_main.coll_id = r_coll_main.coll_id "); + query.append(" ) AS fileProperties "); + query.append("WHERE "); + + return query.toString(); + } + + private String buildSelectClauseForCollectionsForPropertiesSearch() { + StringBuilder query = new StringBuilder(); + query.append("SELECT * FROM ( "); + query.append(" SELECT"); + query.append(" replace(r_coll_main.coll_name, r_coll_main.parent_coll_name || '/', '') AS name, "); + query.append(" 0 AS repl_num, "); + query.append(" r_coll_main.coll_owner_name AS owner_name, "); + query.append(" r_coll_main.coll_owner_zone AS owner_zone, "); + query.append(" 0 AS size, "); + query.append(" '' AS resc_name, "); + query.append(" r_coll_main.coll_name AS path, "); + query.append(" '' AS checksum, "); + query.append(" CAST(r_coll_main.create_ts AS BIGINT), "); + query.append(" CAST(r_coll_main.modify_ts AS BIGINT) "); + query.append(" FROM"); + query.append(" r_coll_main "); + query.append(" ) AS fileProperties "); + query.append("WHERE "); + + return query.toString(); + } + + @Override + public String buildSelectTotalDataObjectsUnderPathThatMatchSearchText(String parentPath, String searchText) { + StringBuilder query = new StringBuilder(); + query.append(" WITH countDataObjectsThatMatchSearchText AS ( "); + query.append(" select distinct on (d.data_name) "); + query.append(" d.data_name,"); + query.append(" d.data_id,"); + query.append(" d.data_repl_num,"); + query.append(" d.data_version,"); + query.append(" d.data_type_name,"); + query.append(" d.data_size,"); + query.append(" d.resc_group_name,"); + query.append(" d.resc_name,"); + query.append(" d.data_path,"); + query.append(" d.data_owner_name,"); + query.append(" d.data_owner_zone,"); + query.append(" d.data_is_dirty,"); + query.append(" d.data_status,"); + query.append(" d.data_checksum,"); + query.append(" d.data_expiry_ts,"); + query.append(" d.data_map_id,"); + query.append(" d.data_mode,"); + query.append(" d.r_comment,"); + query.append(" d.create_ts,"); + query.append(" d.modify_ts,"); + query.append(" d.resc_hier "); + query.append("from "); + query.append(" R_DATA_MAIN d, "); + query.append(" R_COLL_MAIN c "); + query.append("where "); + query.append(" c.coll_id = d.coll_id "); + query.append(" AND "); + query.append(" c.coll_name = ? "); + query.append(" AND "); + query.append(" d.data_name ILIKE ? "); + query.append(" ) "); + query.append(" SELECT COUNT(*) FROM countDataObjectsThatMatchSearchText "); + return query.toString(); + } + + @Override + public String buildSelectTotalCollectionsUnderPathThatMatchSearchText(String parentPath, String searchText) { + StringBuilder query = new StringBuilder(); + query.append("WITH searchMatchForCollections AS ("); + query.append(" select "); + query.append(" c.coll_id,"); + query.append(" c.coll_name,"); + query.append(" c.parent_coll_name,"); + query.append(" c.coll_owner_name,"); + query.append(" c.coll_owner_zone,"); + query.append(" c.coll_inheritance,"); + query.append(" c.coll_type,"); + query.append(" c.r_comment,"); + query.append(" c.create_ts,"); + query.append(" c.modify_ts "); + query.append("from "); + query.append(" R_COLL_MAIN c "); + query.append("where "); + query.append(" c.coll_name ILIKE ?"); + query.append(" and "); + query.append(" c.parent_coll_name = ? "); + query.append(") "); + query.append("SELECT COUNT(*) "); + query.append("FROM searchMatchForCollections "); + return query.toString(); + } + + @Override + public String buildSelectDataObjectsUnderPathThatMatchSearchText(String parentPath, String searchText, int offset, + int limit, int orderColumn, String orderDir) { + StringBuilder query = new StringBuilder(); + query.append(" select "); + query.append(" data_name,"); + query.append(" coll_name,"); + query.append(" data_id,"); + query.append(" data_size,"); + query.append(" data_path,"); + query.append(" data_owner_name,"); + query.append(" data_owner_zone,"); + query.append(" create_ts,"); + query.append(" modify_ts "); + query.append(" from ( "); + query.append(" select distinct on (d.data_name) "); + query.append(" d.* , "); + query.append(" c.coll_name "); + query.append(" from "); + query.append(" R_DATA_MAIN d "); + query.append(" left join "); + query.append(" R_COLL_MAIN c "); + query.append(" on (c.coll_id = d.coll_id) "); + query.append(" where "); + query.append(" c.coll_name = ? "); + query.append(" AND "); + query.append(" d.data_name ILIKE ? "); + query.append(" ) searchDataObjsByMetadata "); + query.append(" order by " + MiscSpecificQueryUtils.getMapColumnsForDataObjects().get(orderColumn) + " " + + orderDir + " "); + query.append(" offset ? "); + query.append(" limit ? "); + return query.toString(); + } + + @Override + public String buildSelectCollectionsUnderPathThatMatchSearchText(String parentPath, String searchText, int offset, + int limit, int orderColumn, String orderDir) { + // Build specific query SQL command to retrieve data objects + // on the collection taking the offset and limit into account. + StringBuilder query = new StringBuilder(); + query.append("select "); + query.append(" c.coll_id,"); + query.append(" c.coll_name,"); + query.append(" c.parent_coll_name,"); + query.append(" c.coll_owner_name,"); + query.append(" c.coll_owner_zone,"); + query.append(" c.coll_inheritance,"); + query.append(" c.coll_type,"); + query.append(" c.r_comment,"); + query.append(" c.create_ts,"); + query.append(" c.modify_ts "); + query.append("from "); + query.append(" R_COLL_MAIN c "); + query.append("where "); + query.append(" c.coll_name ILIKE ?"); + query.append(" and "); + query.append(" c.parent_coll_name = ? "); + query.append("order by " + MiscSpecificQueryUtils.getMapColumnsForCollections().get(orderColumn) + " " + + orderDir + " "); + query.append("offset ? "); + query.append("limit ? "); + return query.toString(); + + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryConstants.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryConstants.java new file mode 100644 index 000000000..c4110f9a1 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryConstants.java @@ -0,0 +1,19 @@ +/** + * + */ +package com.emc.metalnx.services.irods.utils; + +/** + * Misc constants for specific query processing + * + * @author Mike Conway - NIEHS + * + */ +public class SpecificQueryConstants { + + public static final String propFieldsRegex = "([^A-Za-z0-9-_.,=! ]+)"; + // this regex is used to remove quotes, double quotes and semi-colon from value + // field, so that sql injection becomes harder to happen + public static final String regexForValue = "/[^'\";]/g"; + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryProvider.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryProvider.java new file mode 100644 index 000000000..881bb271a --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryProvider.java @@ -0,0 +1,184 @@ +/** + * + */ +package com.emc.metalnx.services.irods.utils; + +import java.util.List; + +import com.emc.metalnx.core.domain.entity.DataGridFilePropertySearch; +import com.emc.metalnx.core.domain.entity.DataGridMetadataSearch; +import com.emc.metalnx.core.domain.entity.enums.DataGridSearchOperatorEnum; +import com.emc.metalnx.core.domain.entity.enums.FilePropertyField; + +/** + * Represents a provider of specific query sql for a given database flavor, + * creating sql statements that can be used via specific query + * + * @author Mike Conway - NIEHS + * + */ +public interface SpecificQueryProvider { + + /** + * Build query for the number of data objects that match the given search term. + * + * @param parentPath + * String path to the parent collection where you are + * looking for items that match a search term + * @param searchText + * String term to be matched + * @return String with the SQL text + */ + public String buildSelectTotalDataObjectsUnderPathThatMatchSearchText(String parentPath, String searchText); + + /** + * Build query for the number of collections that match the given search term. + * + * @param parentPath + * String path to the parent collection where you are + * looking for items that match a search term + * @param searchText + * String term to be matched + * @return String with the SQL text + */ + public String buildSelectTotalCollectionsUnderPathThatMatchSearchText(String parentPath, String searchText); + + /** + * Build collection service query of data objects under a parent that match the + * search text + * + * @param parentPath + * String path to the parent collection where you are + * looking for items that match a search term + * @param searchText + * String with term to be matched + * @param offset + * int with partial start index + * @param limit + * int max number of items retrieved + * @return String with the SQL text + **/ + public String buildSelectDataObjectsUnderPathThatMatchSearchText(String parentPath, String searchText, int offset, + int limit, int orderColumn, String orderDir); + + /** + * Build collection service query of collections under a parent that match the + * search text + * + * @param parentPath + * String path to the parent collection where you are + * looking for items that match a search term + * @param searchText + * String with term to be matched + * @param offset + * int with partial start index + * @param limit + * int max number of items retrieved + * @return String with the SQL text + **/ + public String buildSelectCollectionsUnderPathThatMatchSearchText(String parentPath, String searchText, int offset, + int limit, int orderColumn, String orderDir); + + /** + * Creates a specific query based on the metadata search criteria. + * + * @param metadataSearch + * List of {@link DataGridMetadataSearch} with the user + * supplied search terms search criteria + * @param zone + * String zone to be looking for data objects or + * collections + * @param searchAgainstColls + * boolean Flag set to true when looking + * for collections. false when looking for data objects. + * @return String with the SQL text + */ + String buildSpecificQueryForMetadataSearch(List metadataSearch, String zone, + boolean searchAgainstColls); + + /** + * For a data grid property search, build a 'where' clause for the appropriate + * database flavor + * + * @param attribute + * {@link FilePropertyField} specifying the data grid property search + * @param operator + * {@link DataGridSearchOperatorEnum} with the operator for the query + * @param value + * String with the value for the search + * @return String with the SQL text + */ + String buildWhereClauseForDataGridPropertySearch(final FilePropertyField attribute, + final DataGridSearchOperatorEnum operator, final String value); + + /** + * Given a query, add the offset and limit sql terms to the end of the query + * + * @param query + * String with the database query + * @param offset + * int with the offset + * @param limit + * int with the limit + * @return String with the appended offset and limit bits + */ + String addOffsetAndLimitToQuery(final String query, final int offset, final int limit); + + /** + * build the query for obtaining a count of items matching a metadata search + * + * @param metadataSearch + * List of {@link DataGridMetadataSearch} with the user + * supplied search terms search criteria + * @param zone + * String zone to be looking for data objects or + * collections + * @param searchAgainstColls + * boolean Flag set to true when looking + * for collections. false when looking for data objects. + * @return String with the appropriate sql query + */ + String buildQueryForCountOfItemsMatchingMetadataSearch(final List metadataSearch, + final String zone, final boolean searchAgainstColls); + + /** + * build the query for obtaining a count of items matching a file properties + * search + * + * @param filePropertiesSearch + * List of {@link DataGridFilePropertySearch} that + * constitute the query + * @param zone + * String zone to be looking for data objects or + * collections + * @param searchAgainstColls + * boolean Flag set to true when looking + * for collections. false when looking for data objects. + * @return String with the appropriate sql query + */ + String buildQueryCountItemsMatchingPropertiesSearch(final List filePropertiesSearch, + final String zone, final boolean searchAgainstColls); + + /** + * Build a specific query sql string for searching by file properties, based on + * the search type + * + * @param filePropertiesSearch + * List of {@link DataGridFilePropertySearch} that + * constitute the query + * @param zone + * String zone to be looking for data objects or + * collections + * @param searchAgainstColls + * boolean Flag set to true when looking + * for collections. false when looking for data objects. + * @param offset + * int with query offset + * @param limit + * int with query results limit + * @return String with the appropriate sql query + */ + String buildQueryForFilePropertiesSearch(List filePropertiesSearches, String zone, + boolean searchAgainstColls, int offset, int limit); + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryProviderFactory.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryProviderFactory.java new file mode 100644 index 000000000..5dd36267e --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryProviderFactory.java @@ -0,0 +1,43 @@ +package com.emc.metalnx.services.irods.utils; + +import org.irods.jargon.core.protovalues.IcatTypeEnum; +import org.irods.jargon.core.pub.domain.ClientHints; + +import com.emc.metalnx.core.domain.exceptions.UnsupportedDataGridFeatureException; + +/** + * Represents a factory that can produce specific query generating services for + * a given database flavor + * + * @author Mike Conway - NIEHS + * + */ +public interface SpecificQueryProviderFactory { + + /** + * Given iRODS {@link ClientHints} return the provider that can create + * appropriate sql statements + * + * @param clientHints + * {@link ClientHints} + * @return {@link SpecificQueryProvider} to create various sql statements needed + * by metalnx + * @throws UnsupportedDataGridFeatureException + * if the provider cannot be found + */ + SpecificQueryProvider instance(ClientHints clientHints) throws UnsupportedDataGridFeatureException; + + /** + * Given iRODS {@link icatTypeEnum} return the provider that can create + * appropriate sql statements + * + * @param icatTypeEnum + * {@link icatTypeEnum} + * @return {@link SpecificQueryProvider} to create various sql statements needed + * by metalnx + * @throws UnsupportedDataGridFeatureException + * if the provider cannot be found + */ + SpecificQueryProvider instance(IcatTypeEnum icatTypeEnum) throws UnsupportedDataGridFeatureException; + +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryProviderFactoryImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryProviderFactoryImpl.java new file mode 100644 index 000000000..d90e525e8 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/SpecificQueryProviderFactoryImpl.java @@ -0,0 +1,59 @@ +/** + * + */ +package com.emc.metalnx.services.irods.utils; + +import org.irods.jargon.core.protovalues.IcatTypeEnum; +import org.irods.jargon.core.pub.domain.ClientHints; + +import com.emc.metalnx.core.domain.exceptions.UnsupportedDataGridFeatureException; + +/** + * Create the appropriate SpecificQueryProvider + * + * @author Mike Conway - NIEHS + * + */ +public class SpecificQueryProviderFactoryImpl implements SpecificQueryProviderFactory { + + /* + * (non-Javadoc) + * + * @see + * com.emc.metalnx.services.irods.utils.SpecificQueryProviderFactory#instance( + * org.irods.jargon.core.pub.domain.ClientHints) + */ + @Override + public SpecificQueryProvider instance(final ClientHints clientHints) throws UnsupportedDataGridFeatureException { + if (clientHints == null) { + throw new IllegalArgumentException("null clientHints"); + } + + return instance(clientHints.whatTypeOfIcatIsIt()); + + } + + @Override + public SpecificQueryProvider instance(final IcatTypeEnum icatTypeEnum) throws UnsupportedDataGridFeatureException { + if (icatTypeEnum == null) { + throw new IllegalArgumentException("null icatTypeEnum"); + } + + SpecificQueryProvider provider = null; + + switch (icatTypeEnum) { + case POSTGRES: + provider = new PostgresSpecificQueryProviderImpl(); + break; + case MYSQL: + provider = null; + break; + default: + throw new UnsupportedDataGridFeatureException("unable to handle specific queries to database"); + } + + return provider; + + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/package-info.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/package-info.java new file mode 100644 index 000000000..245880dc6 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/irods/utils/package-info.java @@ -0,0 +1,8 @@ + +/** + * Utilities and support for iRODS services + * + * @author Mike Conway - NIEHS + * + */ +package com.emc.metalnx.services.irods.utils; \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/MachineInfoServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/MachineInfoServiceImpl.java new file mode 100755 index 000000000..9ba35e18c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/MachineInfoServiceImpl.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine; + +import com.emc.metalnx.services.interfaces.MachineInfoService; +import org.springframework.stereotype.Service; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +@Service +public class MachineInfoServiceImpl implements MachineInfoService { + + @Override + public String getAddress(String hostName) throws UnknownHostException { + + InetAddress inetAddress = InetAddress.getByName(hostName); + + return inetAddress.getHostAddress(); + } + + @Override + public String getHostName(String ip) throws UnknownHostException { + InetAddress inetAddress = InetAddress.getByName(ip); + + return inetAddress.getHostName(); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/MonitoringServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/MonitoringServiceImpl.java new file mode 100755 index 000000000..639e247ef --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/MonitoringServiceImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine; + +import com.emc.metalnx.services.interfaces.MonitoringService; +import com.emc.metalnx.services.machine.enums.ServerRequestInfoType; +import com.emc.metalnx.services.machine.util.ServerUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Service +public class MonitoringServiceImpl implements MonitoringService { + + @Value("${rmd.connection.port}") + private String rmdConnectionPort; + + @Value("${rmd.connection.timeout}") + private String rmdConnectionTimeout; + + private static final Logger logger = LoggerFactory.getLogger(MonitoringServiceImpl.class); + + @Override + public String getDataFromHost(String type, String host) { + logger.debug("Making {} request to {}.", type, host); + Integer timeout = Integer.parseInt(rmdConnectionTimeout); + ServerRequestInfoType infoType = ServerRequestInfoType.valueOf(type); + return ServerUtil.getMachineInformation(host, rmdConnectionPort, infoType, timeout); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/ServerServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/ServerServiceImpl.java new file mode 100755 index 000000000..b8b42dc64 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/ServerServiceImpl.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.MachineInfoService; +import com.emc.metalnx.services.interfaces.ResourceService; +import com.emc.metalnx.services.interfaces.ServerService; +import com.emc.metalnx.services.interfaces.StorageService; +import com.emc.metalnx.services.machine.enums.ServerRequestInfoType; +import com.emc.metalnx.services.machine.util.DataGridServerStatusComparator; +import com.emc.metalnx.services.machine.util.ServerInformationRetrievalThread; +import com.emc.metalnx.services.machine.util.ServerUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.util.*; + +@Service +public class ServerServiceImpl implements ServerService { + + @Autowired + ResourceService resourceService; + + @Autowired + StorageService storageService; + + @Autowired + MachineInfoService machineInfoService; + + @Value("${rmd.connection.timeout}") + private String rmdConnectionTimeout; + + @Value("${rmd.connection.port}") + private String rmdConnectionPort; + + @Value("${reverse.dns.lookup}") + private String reverseDnsLookup; + + // Cache attributes + private Hashtable serverInfoCache = new Hashtable(); + private Hashtable cacheTimeToLive = new Hashtable(); + + private List threadList = new ArrayList(); + + private static final Logger logger = LoggerFactory.getLogger(ServerServiceImpl.class); + + @Override + public List getAllServers(List resources, HashMap serverMapInCache) + throws DataGridConnectionRefusedException { + logger.info("Getting the list of all servers of the Grid"); + + List servers = resourceService.getAllResourceServers(resources); + + initServerThreads(servers); + + for (DataGridServer server : servers) { + String serverHostName = server.getHostname(); + + List serverResources = null; + + // Getting general status of the server + + String serverInfo = getServerInfo(serverHostName); + ObjectMapper mapper = new ObjectMapper(); + String machineStatus = null; + String diskInfo = null; + String rmdInfo = null; + try { + JsonNode json = mapper.readTree(serverInfo); + machineStatus = json.get("serverstatus").toString(); + diskInfo = json.get("disk").toString(); + rmdInfo = json.get("version").toString(); + + serverResources = serverMapInCache.get(serverHostName).getResources(); + } + catch (IOException | NullPointerException e) { + logger.error("Could not parse server information: {}", e.getMessage()); + } + + ServerUtil.populateDataGridServerStatus(machineStatus, server); + ServerUtil.setDataGridServerRMDInfo(rmdInfo, server); + + // Retrieving storage info + long totalStorageAvailable = storageService.totalAvailableStorageOfAServer(serverHostName, diskInfo, serverResources); + + long totalStorageUsed = storageService.totalUsedStorageOfAServer(serverHostName, diskInfo, serverResources); + + long totalStorage = totalStorageAvailable + totalStorageUsed; + + server.setTotalStorage(totalStorage); + server.setTotalStorageAvailable(totalStorageAvailable); + server.setTotalStorageUsed(totalStorageUsed); + } + + Collections.sort(servers, new DataGridServerStatusComparator()); + + return servers; + } + + @Override + public List getAllIsilonServers(List resources) { + logger.info("Getting the list of all isilon servers of the Grid"); + List isilonServers = resourceService.getAllIsilonServers(resources); + + Collections.sort(isilonServers, new DataGridServerStatusComparator()); + + return isilonServers == null || isilonServers.isEmpty() ? null : isilonServers; + } + + @Override + public List getAllNonResourceServers(List servers, HashMap serverMapInCache) + throws DataGridConnectionRefusedException { + logger.info("Getting the list of all non-resource servers from the Grid"); + return getAllNonResourceServers(servers, serverMapInCache, null); + } + + @Override + public List getAllNonResourceServers(List servers, HashMap serverMapInCache, + List dataGridResources) throws DataGridConnectionRefusedException { + logger.info("Getting the list of all non-resource servers from the Grid using Resource cache"); + + List nonResourceServers = new ArrayList(); + + for (DataGridServer server : servers) { + + if (server.getHostname().compareToIgnoreCase("localhost") == 0) { + continue; + } + + ObjectMapper mapper = new ObjectMapper(); + String mounts = null; + try { + String serverInfo = getServerInfo(server.getHostname()); + JsonNode json = mapper.readTree(serverInfo); + mounts = json.get("mounts").toString(); + } + catch (JsonProcessingException e) { + logger.error("Could not parse server information", e.getMessage()); + } + catch (IOException e) { + logger.error("Could not parse server information", e.getMessage()); + } + + if (mounts != null && !mounts.isEmpty()) { + HashMap hashMap = ServerUtil.getNFSMountMap(mounts); + List resources = null; + + try { + logger.info("Getting resources of a server {}", server.getHostname()); + resources = resourceService.getResourcesOfAServer(server.getHostname(), dataGridResources); + } + catch (DataGridConnectionRefusedException e) { + // if it is not possible to retrieve all resources existing in a server due to + // a connect exception, we will need to use the resources stored in cache, if + // any + if (serverMapInCache != null) { + resources = serverMapInCache.get(server.getHostname()).getResources(); + } + else { + resources = new ArrayList(); + } + } + + for (DataGridResource resource : resources) { + if (hashMap.containsKey(resource.getPath())) { + String host = hashMap.get(resource.getPath()); + DataGridServer newServer = new DataGridServer(); + try { + host = reverseDnsLookup.compareTo("true") == 0 ? machineInfoService.getHostName(host) : host; + newServer.setHostname(host); + newServer.setIp(machineInfoService.getAddress(host)); + } + catch (UnknownHostException e) { + logger.error("Could not resolve IP address for " + host); + } + + if (!nonResourceServers.contains(newServer)) { + nonResourceServers.add(newServer); + } + } + } + } + + } + + return nonResourceServers.isEmpty() ? null : nonResourceServers; + } + + /********************************************************************************************/ + /* CACHE METHODS */ + /********************************************************************************************/ + + private void initServerThreads(List servers) { + for (DataGridServer server : servers) { + + // Instantiating thread + ServerInformationRetrievalThread t = new ServerInformationRetrievalThread(server.getHostname(), rmdConnectionPort, + ServerRequestInfoType.ALL, Integer.parseInt(rmdConnectionTimeout)); + + // Starting thread execution + t.start(); + + // Keeping track of the thread instance + synchronized (threadList) { + threadList.add(t); + } + } + + synchronized (threadList) { + for (ServerInformationRetrievalThread t : threadList) { + try { + t.join(); + String results = t.getResult(); + + if (results == null) { + results = ""; + } + + serverInfoCache.put(t.getServerHost(), results); + cacheTimeToLive.put(t.getServerHost(), System.currentTimeMillis() + 5 * 1000); + + } + catch (InterruptedException e) { + logger.error("Could not get server information on [{}]", t.getServerHost(), e); + } + } + } + + synchronized (threadList) { + threadList.clear(); + } + } + + private String getServerInfo(String hostname) { + logger.debug("Getting Server Info for [{}]", hostname); + long currentTime = System.currentTimeMillis(); + + if (serverInfoCache.containsKey(hostname) && currentTime < cacheTimeToLive.get(hostname)) { + logger.debug("Cache hit for [{}]", hostname); + return serverInfoCache.get(hostname); + } + else if (currentTime > cacheTimeToLive.get(hostname)) { + + // Invalidating entries + serverInfoCache.remove(hostname); + cacheTimeToLive.remove(hostname); + } + + logger.debug("Cache miss for [{}]", hostname); + logger.info("Getting all metrics from [{}]", hostname); + + ServerInformationRetrievalThread serverInfoThread = new ServerInformationRetrievalThread(hostname, rmdConnectionPort, + ServerRequestInfoType.ALL, Integer.parseInt(rmdConnectionTimeout)); + serverInfoThread.start(); + try { + serverInfoThread.join(); + } + catch (InterruptedException e) { + logger.error("Could not get server information on [{}]", serverInfoThread.getServerHost(), e); + } + + String results = serverInfoThread.getResult(); + + if (results != null) { + serverInfoCache.put(hostname, results); + + // Settings time to live for entry + cacheTimeToLive.put(hostname, System.currentTimeMillis() + 5 * 1000); + } + + return results; + } + + /********************************************************************************************/ + /* END CACHE METHODS */ + /********************************************************************************************/ + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/StorageServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/StorageServiceImpl.java new file mode 100755 index 000000000..eba8ab7f2 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/StorageServiceImpl.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.ResourceService; +import com.emc.metalnx.services.interfaces.StorageService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +@Service +public class StorageServiceImpl implements StorageService { + @Autowired + ResourceService resourceService; + + // block size + private int blockSize = 1024; + + private static final Logger logger = LoggerFactory.getLogger(StorageServiceImpl.class); + + @Override + public long totalUsedStorageOfGrid(List servers) { + logger.info("Get total used storage of the grid"); + + long totalUsedStorage = 0; + + for (DataGridServer dataGridServer : servers) { + totalUsedStorage += dataGridServer.getTotalStorageUsed(); + } + + return totalUsedStorage; + } + + @Override + public long totalAvailableStorageOfGrid(List servers) { + logger.info("Get total available storage of the grid"); + + long totalAvailableStorage = 0; + + for (DataGridServer dataGridServer : servers) { + totalAvailableStorage += dataGridServer.getTotalStorageAvailable(); + } + + return totalAvailableStorage; + } + + @Override + public long totalUsedStorageOfAServer(String hostname, String diskInfoJson, List currentServerResources) + throws DataGridConnectionRefusedException { + logger.info("Get total used storage of a specific server {}", hostname); + + long totalUsed = 0; + + if (hostname == null || diskInfoJson == null || hostname.isEmpty() || diskInfoJson.isEmpty()) { + return totalUsed; + } + + HashMap usedMap = null; + List dataGridResources = null; + + try { + dataGridResources = resourceService.getResourcesOfAServer(hostname, currentServerResources); + } + catch (DataGridConnectionRefusedException e) { + logger.error("Could not get resources from server ", hostname); + + // if it is no possible to retrieve the resources of a server due to a connect + // exception, + // we will use all known resources of this server stored in cache + if (currentServerResources != null) { + dataGridResources = currentServerResources; + } + else { + dataGridResources = new ArrayList(); + } + } + + try { + usedMap = createMapPartitionAndAmountUsed(diskInfoJson); + + for (String mountedOn : usedMap.keySet()) { + boolean foundResc = false; + for (DataGridResource dataGridResource : dataGridResources) { + String resourcePath = dataGridResource.getPath(); + if (resourcePath.startsWith(mountedOn)) { + totalUsed += Long.parseLong(usedMap.get(mountedOn)); + foundResc = true; + dataGridResources.remove(dataGridResource); + break; + } + } + if (foundResc) { + continue; + } + } + + } + catch (JsonProcessingException e) { + logger.info("Could not parse total used storage information for: ", hostname); + } + catch (IOException e) { + logger.info("Could not parse total used storage information for: ", hostname); + } + catch (NumberFormatException e) { + logger.info("Could not format String:{} ", e.getMessage()); + } + + return totalUsed * blockSize; + } + + @Override + public long totalAvailableStorageOfAServer(String hostname, String diskInfoJson, List currentServerResources) + throws DataGridConnectionRefusedException { + logger.info("Get total available storage of a specific server {}", hostname); + + long totalAvailable = 0; + + if (hostname == null || diskInfoJson == null || hostname.isEmpty() || diskInfoJson.isEmpty()) { + return totalAvailable; + } + + HashMap availableMap = null; + List dataGridResources = null; + + try { + dataGridResources = resourceService.getResourcesOfAServer(hostname, currentServerResources); + } + catch (DataGridConnectionRefusedException e) { + logger.error("Could not get resources from server ", hostname); + + // if it is no possible to retrieve the resources of a server due to a connect + // exception, + // we will use all known resources of this server stored in cache + if (currentServerResources != null) { + dataGridResources = currentServerResources; + } + else { + dataGridResources = new ArrayList(); + } + } + + try { + availableMap = createMapPartitionAndAmountAvailable(diskInfoJson); + + for (String mountedOn : availableMap.keySet()) { + boolean foundResc = false; + for (DataGridResource dataGridResource : dataGridResources) { + String resourcePath = dataGridResource.getPath(); + if (resourcePath.startsWith(mountedOn)) { + totalAvailable += Long.parseLong(availableMap.get(mountedOn)); + foundResc = true; + dataGridResources.remove(dataGridResource); + break; + } + } + if (foundResc) { + continue; + } + } + + } + catch (JsonProcessingException e) { + logger.info("Could not parse total available storage information for: ", hostname); + } + catch (IOException e) { + logger.info("Could not parse total available storage information for: ", hostname); + } + catch (NumberFormatException e) { + logger.info("Could not format String:{} ", e.getMessage()); + } + + return totalAvailable * blockSize; + } + + /** + * Creates a Hash mapping where a partition is mounted and the amount of used storage + * + * @param partitionsLocation + * JSON with machine disk status + * @return Hash Map + * @throws IOException + * @throws JsonProcessingException + */ + private HashMap createMapPartitionAndAmountUsed(String partitionsLocation) throws JsonProcessingException, IOException { + + ObjectMapper mapper = new ObjectMapper(); + JsonNode json = mapper.readTree(partitionsLocation); + + HashMap partNameMountedMap = new HashMap(); + + Iterator fieldNames = json.fieldNames(); + while (fieldNames.hasNext()) { + JsonNode jo = json.get(fieldNames.next()); + String mounted_on = jo.get("mounted_on").textValue(); + String used = jo.get("used").textValue(); + partNameMountedMap.put(mounted_on, used); + } + + return partNameMountedMap; + } + + /** + * Creates a Hash mapping where a partition is mounted and the amount of available storage + * + * @param partitionsLocation + * JSON with machine disk status + * @return + * @throws IOException + * @throws JsonProcessingException + */ + private HashMap createMapPartitionAndAmountAvailable(String partitionsLocation) throws JsonProcessingException, IOException { + + ObjectMapper mapper = new ObjectMapper(); + JsonNode json = mapper.readTree(partitionsLocation); + + HashMap partNameMountedMap = new HashMap(); + + Iterator fieldNames = json.fieldNames(); + while (fieldNames.hasNext()) { + JsonNode jo = json.get(fieldNames.next()); + String mounted_on = jo.get("mounted_on").textValue(); + String available = jo.get("available").textValue(); + partNameMountedMap.put(mounted_on, available); + } + + return partNameMountedMap; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/enums/ServerRequestInfoType.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/enums/ServerRequestInfoType.java new file mode 100755 index 000000000..2a4240ebe --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/enums/ServerRequestInfoType.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine.enums; + +public enum ServerRequestInfoType { + + ALL(""), + GENERAL_STATUS("serverstatus"), + CPU_STATS("cpustat"), + CPU_INFO("cpu"), + MEMORY_STATS("memory"), + MOUNT("mounts"), + DISK_STATS("disk"), + IRODS_LOGS("irodslogs"), + IRODS_STATUS("irodsstatus"), + NORMAL_STATUS("normal"), + WARNING_STATUS("warning"), + ERROR_STATUS("error"); + + private String infoType; + + private ServerRequestInfoType(String infoType) { + this.infoType = infoType; + } + + public String toString() { + return this.infoType; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/enums/SpecQueryEnum.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/enums/SpecQueryEnum.java new file mode 100755 index 000000000..960ebccbf --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/enums/SpecQueryEnum.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine.enums; + +public enum SpecQueryEnum { + + SQL_ATTR_EQUAL_VALUE_1_COND("metalnxMetadataAttrEqualValue1Cond"), + SQL_ATTR_NOT_EQUAL_VALUE_1_COND("metalnxMetadataAttrNotEqualValue1Cond"), + SQL_ATTR_LIKE_VALUE_1_COND("metalnxMetadataAttrLikeValue1Cond"), + SQL_ATTR_NOT_LIKE_VALUE_1_COND("metalnxMetadataAttrNotLikeValue1Cond"); + + private String specQueryAlias; + + private SpecQueryEnum(String specQueryAlias) { + this.specQueryAlias = specQueryAlias; + } + + public String toString() { + return this.specQueryAlias; + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/DataGridDataObjectReplicaComparator.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/DataGridDataObjectReplicaComparator.java new file mode 100755 index 000000000..1af83138a --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/DataGridDataObjectReplicaComparator.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine.util; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; + +import java.util.Comparator; + +public class DataGridDataObjectReplicaComparator implements Comparator { + + @Override + public int compare(DataGridCollectionAndDataObject do1, DataGridCollectionAndDataObject do2) { + return do1.getReplicaNumber().compareTo(do2.getReplicaNumber()); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/DataGridServerStatusComparator.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/DataGridServerStatusComparator.java new file mode 100755 index 000000000..1a5ca9692 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/DataGridServerStatusComparator.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine.util; + +import com.emc.metalnx.core.domain.entity.DataGridServer; + +import java.util.Comparator; +import java.util.HashMap; + +public class DataGridServerStatusComparator implements Comparator { + + @Override + public int compare(DataGridServer o1, DataGridServer o2) { + HashMap values = new HashMap(); + values.put("normal", 2); + values.put("warning", 1); + values.put("error", 0); + + if(o1.getMachineStatus() == null || o2.getMachineStatus() == null) { + return 0; + } + + return values.get(o1.getMachineStatus()).compareTo(values.get(o2.getMachineStatus())); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/DataGridUtils.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/DataGridUtils.java new file mode 100755 index 000000000..27766689d --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/DataGridUtils.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine.util; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.services.auth.UserTokenDetails; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.domain.DataObject; +import org.irods.jargon.core.pub.domain.Resource; +import org.irods.jargon.core.query.CollectionAndDataObjectListingEntry; +import org.irods.jargon.core.query.CollectionAndDataObjectListingEntry.ObjectType; +import org.irods.jargon.core.query.IRODSQueryResultRow; +import org.irods.jargon.core.query.SpecificQueryResultSet; +import org.irods.jargon.core.utils.IRODSDataConversionUtil; +import org.irods.jargon.core.utils.MiscIRODSUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.util.*; + +public class DataGridUtils { + private static final Logger logger = LoggerFactory.getLogger(DataGridUtils.class); + + /** + * Maps a query result set coming from a metadata search into a list of collections. + * + * @param queryResultSet + * sql result set returned from the execution of a specific query + * @return List of collections + * @throws JargonException + */ + public static List mapMetadataResultSetToDataGridCollections(SpecificQueryResultSet queryResultSet) + throws JargonException { + + List colls = new ArrayList<>(); + + if (queryResultSet != null) { + List results = queryResultSet.getResults(); + + for (IRODSQueryResultRow row : results) { + DataGridCollectionAndDataObject c = new DataGridCollectionAndDataObject(); + + String collPath = row.getColumn("obj_name"); + String collName = collPath.substring(collPath.lastIndexOf("/") + 1, collPath.length()); + + c.setName(collName); + c.setPath(collPath); + c.setParentPath(row.getColumn("parent_path")); + c.setOwner(row.getColumn("obj_owner")); + c.setCollection(true); + c.setReplicaNumber("-"); + c.setCreatedAt(IRODSDataConversionUtil.getDateFromIRODSValue(row.getColumn("create_ts"))); + c.setModifiedAt(IRODSDataConversionUtil.getDateFromIRODSValue(row.getColumn("modify_ts"))); + c.setNumberOfMatches(Integer.valueOf(row.getColumn("totalMatches"))); + c.setResourceName(row.getColumn("resc_name")); + + colls.add(c); + } + } + + return colls; + } + + /** + * Maps a query result set coming from a metadata search into a list of data objects. + * + * @param queryResultSet + * sql result set returned from the execution of a specific query + * @return List of data objects + * @throws JargonException + */ + public static List mapMetadataResultSetToDataGridObjects(SpecificQueryResultSet queryResultSet) + throws JargonException { + + List objs = new ArrayList(); + + if (queryResultSet != null) { + List results = queryResultSet.getResults(); + + for (IRODSQueryResultRow row : results) { + String objName = row.getColumn("obj_name"); + String parentPath = row.getColumn("parent_path"); + String dataObjDisplaySize = MiscIRODSUtils.humanReadableByteCount(Long.valueOf(row.getColumn("size"))); + + String path = parentPath + "/" + objName; + if (parentPath.compareTo("/") == 0) path = parentPath + objName; + + DataGridCollectionAndDataObject obj = new DataGridCollectionAndDataObject(); + obj.setName(objName); + obj.setPath(path); + obj.setParentPath(parentPath); + obj.setSize(Long.valueOf(row.getColumn("size"))); + obj.setDisplaySize(dataObjDisplaySize); + obj.setOwner(row.getColumn("obj_owner")); + obj.setCollection(false); + obj.setReplicaNumber(row.getColumn("repl_num")); + obj.setCreatedAt(IRODSDataConversionUtil.getDateFromIRODSValue(row.getColumn("create_ts"))); + obj.setModifiedAt(IRODSDataConversionUtil.getDateFromIRODSValue(row.getColumn("modify_ts"))); + obj.setResourceName(row.getColumn("resc_name")); + obj.setNumberOfMatches(Integer.valueOf(row.getColumn("totalMatches"))); + + objs.add(obj); + } + } + + return objs; + } + + /** + * Maps a query result set coming from a file properties search into a list of data objects. + * + * @param queryResultSet + * sql result set returned from the execution of a specific query + * @return List of data objects + * @throws JargonException + */ + public static List mapPropertiesResultSetToDataGridObjects(SpecificQueryResultSet queryResultSet) + throws JargonException { + + List dataGridCollectionAndDataObjects = new ArrayList(); + + if (queryResultSet != null) { + List results = queryResultSet.getResults(); + + for (IRODSQueryResultRow irodsQueryResultRow : results) { + DataGridCollectionAndDataObject dataGridObj = new DataGridCollectionAndDataObject(); + + String objName = irodsQueryResultRow.getColumn(0); + + String dataObjDisplaySize = MiscIRODSUtils.humanReadableByteCount(Long.valueOf(irodsQueryResultRow.getColumn(4))); + + dataGridObj.setName(objName); + dataGridObj.setPath(irodsQueryResultRow.getColumn(6)); + dataGridObj.setSize(Long.valueOf(irodsQueryResultRow.getColumn(4))); + dataGridObj.setDisplaySize(dataObjDisplaySize); + dataGridObj.setOwner(irodsQueryResultRow.getColumn(2)); + dataGridObj.setCollection(dataGridObj.getSize() == 0 ? true : false); + dataGridObj.setReplicaNumber(irodsQueryResultRow.getColumn(1)); + dataGridObj.setCreatedAt(IRODSDataConversionUtil.getDateFromIRODSValue(irodsQueryResultRow.getColumn(8))); + dataGridObj.setModifiedAt(IRODSDataConversionUtil.getDateFromIRODSValue(irodsQueryResultRow.getColumn(9))); + dataGridObj.setNumberOfMatches(0); + dataGridObj.setResourceName(irodsQueryResultRow.getColumn(5)); + dataGridObj.setChecksum(irodsQueryResultRow.getColumn(7)); + + dataGridCollectionAndDataObjects.add(dataGridObj); + } + } + + return dataGridCollectionAndDataObjects; + } + + /** + * Checks if the user logged is admin or nor. + * + * @return true, if the user is a rods admin. False, otherwise. + */ + public static boolean isUserLoggedAdmin() { + // Checking if user is Admin or normal user + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth instanceof UsernamePasswordAuthenticationToken) { + UserTokenDetails userDetails = (UserTokenDetails) auth.getDetails(); + + // If the user is admin + if (userDetails.getUser().isAdmin()) { + return true; + } + } + + return false; + } + + /** + * Maps the COUNT from the specific query into an integer + * for collections + * + * @param queryResultSet + * result set returned from a query + * @return list of data grid objects + * @throws JargonException + */ + public static int mapCountQueryResultSetToInteger(SpecificQueryResultSet queryResultSet) throws JargonException { + int totalNumberOfItems = 0; + + if (queryResultSet != null && !queryResultSet.getResults().isEmpty()) { + IRODSQueryResultRow irodsQueryResultRow = queryResultSet.getResults().get(0); + + totalNumberOfItems = Integer.valueOf(irodsQueryResultRow.getColumnAsIntOrZero(0)); + } + + return totalNumberOfItems; + } + + /** + * Maps the results from the specific query for listing files in a collection into data objects + * for collections + * + * @param queryResultSet + * result set returned from a query + * @return list of data grid objects + * @throws JargonException + */ + public static List mapCollectionQueryResultSetToDataGridObjects(SpecificQueryResultSet queryResultSet) + throws JargonException { + List dataGridCollectionAndDataObjects = new ArrayList(); + + List results = queryResultSet.getResults(); + + for (IRODSQueryResultRow irodsQueryResultRow : results) { + CollectionAndDataObjectListingEntry collectionAndDataObject = new CollectionAndDataObjectListingEntry(); + + collectionAndDataObject.setParentPath(irodsQueryResultRow.getColumn("c.parent_coll_name")); + collectionAndDataObject.setPathOrName(irodsQueryResultRow.getColumn("c.coll_name")); + collectionAndDataObject.setOwnerName(irodsQueryResultRow.getColumn("c.coll_owner_name")); + collectionAndDataObject.setOwnerZone(irodsQueryResultRow.getColumn("c.coll_owner_zone")); + collectionAndDataObject.setObjectType(ObjectType.COLLECTION); + collectionAndDataObject.setCreatedAt(IRODSDataConversionUtil.getDateFromIRODSValue(irodsQueryResultRow.getColumn("c.create_ts"))); + collectionAndDataObject.setModifiedAt(IRODSDataConversionUtil.getDateFromIRODSValue(irodsQueryResultRow.getColumn("c.modify_ts"))); + + dataGridCollectionAndDataObjects.add(collectionAndDataObject); + } + + return dataGridCollectionAndDataObjects; + } + + /** + * Maps the results from the specific query for listing files in a collection into DataGrid + * objects + * + * @param queryResultSet + * @return + * @throws JargonException + */ + public static List mapQueryResultSetToDataGridObjects(SpecificQueryResultSet queryResultSet) + throws JargonException { + List dataGridCollectionAndDataObjects = new ArrayList(); + + List results = queryResultSet.getResults(); + + for (IRODSQueryResultRow irodsQueryResultRow : results) { + CollectionAndDataObjectListingEntry collectionAndDataObject = new CollectionAndDataObjectListingEntry(); + + collectionAndDataObject.setParentPath(irodsQueryResultRow.getColumn("c.coll_name")); + collectionAndDataObject.setPathOrName(irodsQueryResultRow.getColumn("d.data_name")); + collectionAndDataObject.setDataSize(Long.parseLong(irodsQueryResultRow.getColumn("d.data_size"))); + collectionAndDataObject.setOwnerName(irodsQueryResultRow.getColumn("d.data_owner_name")); + collectionAndDataObject.setOwnerZone(irodsQueryResultRow.getColumn("d.data_owner_zone")); + collectionAndDataObject.setModifiedAt(IRODSDataConversionUtil.getDateFromIRODSValue(irodsQueryResultRow.getColumn("d.modify_ts"))); + + dataGridCollectionAndDataObjects.add(collectionAndDataObject); + } + + return dataGridCollectionAndDataObjects; + } + + /** + * Maps the results from the specific query for listing files in a collection into DataGrid + * objects + * + * @param queryResultSet + * @return + * @throws JargonException + */ + public static List mapQueryResultSetToDataGridObjectsForSearch(SpecificQueryResultSet queryResultSet) + throws JargonException { + List dataGridCollectionAndDataObjects = new ArrayList(); + + List results = queryResultSet.getResults(); + + for (IRODSQueryResultRow irodsQueryResultRow : results) { + CollectionAndDataObjectListingEntry collectionAndDataObject = new CollectionAndDataObjectListingEntry(); + + collectionAndDataObject.setId(Integer.valueOf(irodsQueryResultRow.getColumn("data_id"))); + collectionAndDataObject.setPathOrName(irodsQueryResultRow.getColumn("data_name")); + collectionAndDataObject.setParentPath(irodsQueryResultRow.getColumn("coll_name")); + collectionAndDataObject.setObjectType(ObjectType.DATA_OBJECT); + collectionAndDataObject.setDataSize(Long.valueOf(irodsQueryResultRow.getColumn("data_size"))); + collectionAndDataObject.setOwnerName(irodsQueryResultRow.getColumn("data_owner_name")); + collectionAndDataObject.setOwnerZone(irodsQueryResultRow.getColumn("data_owner_zone")); + collectionAndDataObject.setModifiedAt(IRODSDataConversionUtil.getDateFromIRODSValue(irodsQueryResultRow.getColumn("create_ts"))); + collectionAndDataObject.setModifiedAt(IRODSDataConversionUtil.getDateFromIRODSValue(irodsQueryResultRow.getColumn("modify_ts"))); + + dataGridCollectionAndDataObjects.add(collectionAndDataObject); + } + + return dataGridCollectionAndDataObjects; + } + + /** + * Maps a Data Object object from Jargon to the Metalnx representation of Data object + * + * @param dataObject + * data object instance to be mapped + * @return DataGridCollectionAndDataObject instance the given Data object + */ + public static DataGridCollectionAndDataObject getDataGridCollectionAndDataObject(DataObject dataObject) { + DataGridCollectionAndDataObject dataGridCollectionAndDataObject = new DataGridCollectionAndDataObject(); + + dataGridCollectionAndDataObject.setName(dataObject.getDataName()); + dataGridCollectionAndDataObject.setPath(dataObject.getDataPath()); + dataGridCollectionAndDataObject.setReplicaNumber(String.valueOf(dataObject.getDataReplicationNumber())); + dataGridCollectionAndDataObject.setChecksum(dataObject.getChecksum()); + dataGridCollectionAndDataObject.setCreatedAt(dataObject.getCreatedAt()); + dataGridCollectionAndDataObject.setModifiedAt(dataObject.getUpdatedAt()); + dataGridCollectionAndDataObject.setOwner(dataObject.getDataOwnerName()); + dataGridCollectionAndDataObject.setCollection(false); + dataGridCollectionAndDataObject.setDisplaySize(dataObject.getDisplayDataSize()); + dataGridCollectionAndDataObject.setSize(dataObject.getDataSize()); + dataGridCollectionAndDataObject.setResourceName(dataObject.getResourceName()); + + return dataGridCollectionAndDataObject; + } + + public static HashMap buildMapForResourcesNamesAndMountPoints(List resourceList) { + HashMap resourceMap = new HashMap(); + for (Resource resource : resourceList) { + resourceMap.put(resource.getName(), resource.getVaultPath()); + } + return resourceMap; + } + + /** + * Sort Replica-Resource Map by replica number + * + * @param map + * Replica-Resource Map to be sorted + * @return + * Replica-Resource Map sorted + */ + public static Map sortReplicaResourceMap( + Map map) { + + Map sortedMap = new TreeMap( + new Comparator() { + + @Override + public int compare(DataGridCollectionAndDataObject do1, DataGridCollectionAndDataObject do2) { + Integer replicaNumberDO1 = new Integer(do1.getReplicaNumber()); + Integer replicaNumberDO2 = new Integer(do2.getReplicaNumber()); + return replicaNumberDO1.compareTo(replicaNumberDO2); + } + + }); + + sortedMap.putAll(map); + + return sortedMap; + } + + /** + * Maps a CollectionAndDataObjectListingEntry list into a DataGridCollectionAndDataObject list + * + * @param entries + * CollectionAndDataObjectListingEntry objects to map + * @return list of DataGridCollectionAndDataObject objects + */ + public static List mapListingEntryToDataGridCollectionAndDataObject( + List entries) { + logger.debug("Mapping a CollectionAndDataObjectListingEntry list into a " + "DataGridCollectionAndDataObject list"); + + List dataGridCollectionAndDataObjects = new ArrayList(); + + for (CollectionAndDataObjectListingEntry entry : entries) { + if("/".equals(entry.getPathOrName())) continue; + DataGridCollectionAndDataObject dataGridCollectionAndDataObject = mapListingEntryToDataGridCollectionAndDataObject(entry); + dataGridCollectionAndDataObjects.add(dataGridCollectionAndDataObject); + } + + return dataGridCollectionAndDataObjects; + } + + /** + * Maps a CollectionAndDataObjectListingEntry object into a DataGridCollectionAndDataObject + * object + * + * @param entry + * CollectionAndDataObjectListingEntry object to map into DataGridCollectionAndDataObject + * @return instance of DataGridCollectionAndDataObject + */ + public static DataGridCollectionAndDataObject mapListingEntryToDataGridCollectionAndDataObject(CollectionAndDataObjectListingEntry entry) { + logger.debug("Mapping a CollectionAndDataObjectListingEntry into a " + "DataGridCollectionAndDataObject"); + + String entryPath = ""; + + if (entry.isCollection()) { + entryPath = entry.getPathOrName(); + } + else { + entryPath = entry.getParentPath() + "/" + entry.getPathOrName(); + } + + String nodeLabelDisplayValue = entry.getNodeLabelDisplayValue(); + String entryName = nodeLabelDisplayValue == null || nodeLabelDisplayValue.isEmpty() ? entryPath : nodeLabelDisplayValue; + + DataGridCollectionAndDataObject dataGridCollectionAndDataObject = new DataGridCollectionAndDataObject(entryPath, + entryName, entry.getParentPath(), entry.isCollection()); + + dataGridCollectionAndDataObject.setCreatedAt(entry.getCreatedAt()); + dataGridCollectionAndDataObject.setModifiedAt(entry.getModifiedAt()); + dataGridCollectionAndDataObject.setOwner(entry.getOwnerName()); + dataGridCollectionAndDataObject.setDisplaySize(entry.getDisplayDataSize()); + dataGridCollectionAndDataObject.setSize(entry.getDataSize()); + + return dataGridCollectionAndDataObject; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/ServerInformationRetrievalThread.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/ServerInformationRetrievalThread.java new file mode 100755 index 000000000..fcdc2f2f8 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/ServerInformationRetrievalThread.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine.util; + +import com.emc.metalnx.services.machine.enums.ServerRequestInfoType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ServerInformationRetrievalThread extends Thread { + + private String serverHost; + private String serverPort; + private ServerRequestInfoType command; + private String result; + private int timeout; + + private static final Logger logger = LoggerFactory.getLogger(ServerInformationRetrievalThread.class); + + public ServerInformationRetrievalThread(String serverHost, String serverPort, ServerRequestInfoType command, int timeout) { + this.serverHost = serverHost; + this.serverPort = serverPort; + this.command = command; + } + + public void run() { + logger.debug("Getting {} info for server {}", this.command.toString(), this.serverHost); + this.result = ServerUtil.getMachineInformation(this.serverHost, this.serverPort, this.command, this.timeout); + } + + /** + * @return the serverHost + */ + public String getServerHost() { + return serverHost; + } + + /** + * @return the result + */ + public String getResult() { + return result; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/ServerUtil.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/ServerUtil.java new file mode 100755 index 000000000..50163ea1c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/machine/util/ServerUtil.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.machine.util; + +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.services.machine.enums.ServerRequestInfoType; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; + +public class ServerUtil { + + private static final Logger logger = LoggerFactory.getLogger(ServerUtil.class); + + /** + * Get machine information such as disk, memory, and cpu status + * + * @param hostname + * host that we want to get the information from + * @param infoType + * disk, memory, cpu, irodsstatus, irodslogs + * @return JSON + * that contains the status of a machine based on the type + */ + static public String getMachineInformation(String hostname, String port, + ServerRequestInfoType infoType, int timeout) { + try { + + String urlString = "http://" + hostname + ":" + port + "/" + infoType.toString(); + + logger.info("Making HTTP request to {}", urlString); + URL url = new URL(urlString); + + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + httpURLConnection.setConnectTimeout(timeout); + + if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( + httpURLConnection.getInputStream())); + + StringBuffer response = new StringBuffer(); + String temp = ""; + while ((temp = bufferedReader.readLine()) != null) { + response.append(temp); + } + + bufferedReader.close(); + return response.toString(); + } + } + catch (IOException e) { + logger.error("Could not get machine information", e.getLocalizedMessage()); + } + + return null; + } + + /** + * Creates a DataGridServer instance based on JSON response from RMD. + * + * @param jsonResponse response from external service in JSON + * @return dataGridServer + */ + static public void populateDataGridServerStatus(String jsonResponse, DataGridServer dataGridServer) { + + // RMD package isn't available + if (jsonResponse == null || jsonResponse.isEmpty()) { + dataGridServer.setMachineStatus(ServerRequestInfoType.WARNING_STATUS.toString()); + dataGridServer.setDataGridStatus(ServerRequestInfoType.WARNING_STATUS.toString()); + dataGridServer.setMemoryStatus(ServerRequestInfoType.WARNING_STATUS.toString()); + dataGridServer.setDiskStatus(ServerRequestInfoType.WARNING_STATUS.toString()); + dataGridServer.setRmdPackageRunning(false); + return; + } + + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode json = mapper.readTree(jsonResponse); + dataGridServer.setMachineStatus(json.get("server").textValue()); + dataGridServer.setDataGridStatus(json.get("irods_server").textValue()); + dataGridServer.setMemoryStatus(json.get("memory").textValue()); + dataGridServer.setDiskStatus(json.get("disk").textValue()); + dataGridServer.setRmdPackageRunning(true); + } + catch (JsonProcessingException e) { + logger.error("Could not parse server status JSON response", e); + } + catch (IOException e) { + logger.error("Could not parse server status JSON response", e); + } + } + + static public HashMap getNFSMountMap(String mountInfoJSON) { + HashMap hashMap = new HashMap(); + + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonArray = mapper.readTree(mountInfoJSON); + for (int i = 0; i < jsonArray.size(); i++) { + JsonNode jsonObj = jsonArray.get(i); + String type = jsonObj.get("type").textValue(); + + if (type.compareTo("nfs") == 0) { + String localPath = jsonObj.get("local_path").textValue(); + String remoteIP = jsonObj.get("remote_ip").textValue(); + hashMap.put(localPath, remoteIP); + } + } + + } + catch (JsonProcessingException e) { + logger.error("Could not parse server status JSON response", e); + } + catch (IOException e) { + logger.error("Could not parse server status JSON response", e); + } + + return hashMap; + } + + /** + * Gets DataGridServer RMD package information: release number and version. + * + * @param jsonResponse + * @return dataGridServer + */ + static public void setDataGridServerRMDInfo(String jsonResponse, DataGridServer dataGridServer) { + + // RMD package isn't available + if (jsonResponse == null || jsonResponse.isEmpty()) { + dataGridServer.setRmdPackageRelease(null); + dataGridServer.setRmdPackageVersion(null); + return; + } + + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode json = mapper.readTree(jsonResponse); + dataGridServer.setRmdPackageRelease(json.get("release").textValue()); + dataGridServer.setRmdPackageVersion(json.get("version").textValue()); + } + catch (JsonProcessingException e) { + logger.error("Could not parse server status JSON response", e); + } + catch (IOException e) { + logger.error("Could not parse server status JSON response", e); + } + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/zip/ZipServiceImpl.java b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/zip/ZipServiceImpl.java new file mode 100755 index 000000000..fdbccea7b --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/com/emc/metalnx/services/zip/ZipServiceImpl.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.zip; + +import com.emc.metalnx.services.interfaces.ZipService; +import org.irods.jargon.core.exception.FileNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.context.WebApplicationContext; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * Service for zipping directories. + */ +@Service +@Transactional +@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES) +public class ZipServiceImpl implements ZipService { + private static final Logger logger = LoggerFactory.getLogger(ZipServiceImpl.class); + private static final String ZIP_EXTENSION = ".zip"; + + public File createZip(File directoryToPlaceZip, File directoryToZip) { + List files = new ArrayList<>(); + getAllFiles(directoryToZip, files); + return writeZipFile(directoryToPlaceZip, directoryToZip, files); + } + + private void getAllFiles(File dir, List files) { + File[] filesArr = dir.listFiles(); + + if (filesArr == null) { + return; + } + + for (File file: filesArr) { + files.add(file); + if (file.isDirectory()) { + getAllFiles(file, files); + } + } + } + + private File writeZipFile(File directoryToPlaceZip, File directoryToZip, List fileList) { + String zipFileName = String.format("%s/%s%s", directoryToPlaceZip.getName(), + directoryToZip.getName(), ZIP_EXTENSION); + + FileOutputStream fos = null; + ZipOutputStream zos = null; + + try { + fos = new FileOutputStream(zipFileName); + zos = new ZipOutputStream(fos); + + for (File file : fileList) { + if (!file.isDirectory()) { // we only zip files, not directories + addToZip(directoryToZip, file, zos); + } + } + } catch (FileNotFoundException | IOException e) { + logger.error("Could not create zip file"); + } finally { + try { + if (zos != null) zos.close(); + if (fos != null) fos.close(); + } catch (IOException e) { + logger.error("Could not close zip streams: {}", e); + } + } + + return new File(zipFileName); + } + + private static void addToZip(File directoryToZip, File file, ZipOutputStream zos) throws FileNotFoundException, + IOException { + + FileInputStream fis = new FileInputStream(file); + + // we want the zipEntry's path to be a relative path that is relative + // to the directory being zipped, so chop off the rest of the path + String zipFilePath = file.getCanonicalPath().substring(directoryToZip.getCanonicalPath().length() + 1, + file.getCanonicalPath().length()); + System.out.println("Writing '" + zipFilePath + "' to zip file"); + ZipEntry zipEntry = new ZipEntry(zipFilePath); + zos.putNextEntry(zipEntry); + + byte[] bytes = new byte[1024]; + int length; + while ((length = fis.read(bytes)) >= 0) { + zos.write(bytes, 0, length); + } + + zos.closeEntry(); + fis.close(); + } +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/org/irods/jargon/midtier/utils/configuration/MidTierConfiguration.java b/packaging/src/emc-metalnx-services/src/main/java/org/irods/jargon/midtier/utils/configuration/MidTierConfiguration.java new file mode 100644 index 000000000..010e5d0f1 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/org/irods/jargon/midtier/utils/configuration/MidTierConfiguration.java @@ -0,0 +1,134 @@ +/** + * + */ +package org.irods.jargon.midtier.utils.configuration; + +import org.irods.jargon.core.connection.AuthScheme; +import org.irods.jargon.core.connection.ClientServerNegotiationPolicy; + +/** + * Pojo containing configuration information + * + * @author Mike Conway - NIEHS + * + */ +public class MidTierConfiguration { + + private String defaultStorageResource = ""; + + /** + * Utilize the read ahead and write behind streams in jargon to optimize + * transfers + */ + private boolean utilizePackingStreams = true; + + /** + * AuthScheme to use for accounts, based on {@link AuthScheme} + */ + private String authType = AuthScheme.STANDARD.toString(); + + /** + * sets ssl negotiation policy in jargon + */ + private String sslNegotiationPolicy = ClientServerNegotiationPolicy.SslNegotiationPolicy.CS_NEG_DONT_CARE + .toString(); + + /** + * requests, if true, that a checksum be computed on upload + */ + private boolean computeChecksum = false; + + /** + * @return the defaultStorageResource + */ + public String getDefaultStorageResource() { + return defaultStorageResource; + } + + /** + * @param defaultStorageResource + * the defaultStorageResource to set + */ + public void setDefaultStorageResource(final String defaultStorageResource) { + this.defaultStorageResource = defaultStorageResource; + } + + /** + * @return the authType + */ + public String getAuthType() { + return authType; + } + + /** + * @param authType + * the authType to set + */ + public void setAuthType(String authType) { + this.authType = authType; + } + + /** + * @return the utilizePackingStreams + */ + public boolean isUtilizePackingStreams() { + return utilizePackingStreams; + } + + /** + * @param utilizePackingStreams + * the utilizePackingStreams to set + */ + public void setUtilizePackingStreams(boolean utilizePackingStreams) { + this.utilizePackingStreams = utilizePackingStreams; + } + + /** + * @return the sslNegotiationPolicy + */ + public String getSslNegotiationPolicy() { + return sslNegotiationPolicy; + } + + /** + * @param sslNegotiationPolicy + * the sslNegotiationPolicy to set + */ + public void setSslNegotiationPolicy(String sslNegotiationPolicy) { + this.sslNegotiationPolicy = sslNegotiationPolicy; + } + + /** + * @return the computeChecksum + */ + public boolean isComputeChecksum() { + return computeChecksum; + } + + /** + * @param computeChecksum + * the computeChecksum to set + */ + public void setComputeChecksum(boolean computeChecksum) { + this.computeChecksum = computeChecksum; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("MidTierConfiguration ["); + if (defaultStorageResource != null) { + builder.append("defaultStorageResource=").append(defaultStorageResource).append(", "); + } + builder.append("utilizePackingStreams=").append(utilizePackingStreams).append(", "); + if (authType != null) { + builder.append("authType=").append(authType).append(", "); + } + if (sslNegotiationPolicy != null) { + builder.append("sslNegotiationPolicy=").append(sslNegotiationPolicy).append(", "); + } + builder.append("computeChecksum=").append(computeChecksum).append("]"); + return builder.toString(); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/org/irods/jargon/midtier/utils/configuration/StartupConfigurator.java b/packaging/src/emc-metalnx-services/src/main/java/org/irods/jargon/midtier/utils/configuration/StartupConfigurator.java new file mode 100644 index 000000000..28d2dac3f --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/org/irods/jargon/midtier/utils/configuration/StartupConfigurator.java @@ -0,0 +1,115 @@ +/** + * + */ +package org.irods.jargon.midtier.utils.configuration; + +import org.irods.jargon.core.connection.ClientServerNegotiationPolicy; +import org.irods.jargon.core.connection.ClientServerNegotiationPolicy.SslNegotiationPolicy; +import org.irods.jargon.core.connection.IRODSSession; +import org.irods.jargon.core.connection.SettableJargonProperties; +import org.irods.jargon.core.pub.IRODSAccessObjectFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + * @author Mike Conway Wired-in class that takes configuration and core jargon + * components and injects appropriate configuration into the underlying + * jargon properties system + * + */ +public class StartupConfigurator { + + private MidTierConfiguration midTierConfiguration; + private IRODSSession irodsSession; + private IRODSAccessObjectFactory irodsAccessObjectFactory; + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + public StartupConfigurator() { + + } + + /** + * @return the midTierConfiguration + */ + public MidTierConfiguration getMidTierConfiguration() { + return midTierConfiguration; + } + + /** + * @param midTierConfiguration + * the midTierConfiguration to set + */ + public void setMidTierConfiguration(MidTierConfiguration midTierConfiguration) { + this.midTierConfiguration = midTierConfiguration; + } + + /** + * @return the irodsSession + */ + public IRODSSession getIrodsSession() { + return irodsSession; + } + + /** + * @param irodsSession + * the irodsSession to set + */ + public void setIrodsSession(IRODSSession irodsSession) { + this.irodsSession = irodsSession; + } + + /** + * this method is wired into the spring config after the injection of the props + * and IRODSSession so that property configuration can be + * accomplished + */ + public void init() { + log.info("init()"); + + if (midTierConfiguration == null) { + log.error("null midTierConfiguration"); + throw new IllegalStateException("null midTierConfiguration"); + } + + if (irodsSession == null) { + log.error("null irodsSession"); + throw new IllegalStateException("null irodsSession"); + } + + log.info("configuration with:{}", midTierConfiguration); + + SettableJargonProperties props = new SettableJargonProperties(irodsSession.getJargonProperties()); + props.setComputeChecksumAfterTransfer(midTierConfiguration.isComputeChecksum()); + log.info("set checksum policy to:{}", midTierConfiguration.isComputeChecksum()); + + SslNegotiationPolicy policyToSet = ClientServerNegotiationPolicy + .findSslNegotiationPolicyFromString(midTierConfiguration.getSslNegotiationPolicy()); + + log.info("policyToSet:{}", policyToSet); + + props.setNegotiationPolicy(policyToSet); + log.info("negotiation policy set to:{}", props.getNegotiationPolicy()); + + getIrodsSession().setJargonProperties(props); + log.info("config of jargon props complete"); + + } + + /** + * @return the irodsAccessObjectFactory + */ + public IRODSAccessObjectFactory getIrodsAccessObjectFactory() { + return irodsAccessObjectFactory; + } + + /** + * @param irodsAccessObjectFactory + * the irodsAccessObjectFactory to set + */ + public void setIrodsAccessObjectFactory(IRODSAccessObjectFactory irodsAccessObjectFactory) { + this.irodsAccessObjectFactory = irodsAccessObjectFactory; + } + +} diff --git a/packaging/src/emc-metalnx-services/src/main/java/org/irods/jargon/midtier/utils/configuration/package-info.java b/packaging/src/emc-metalnx-services/src/main/java/org/irods/jargon/midtier/utils/configuration/package-info.java new file mode 100644 index 000000000..49d39c5eb --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/java/org/irods/jargon/midtier/utils/configuration/package-info.java @@ -0,0 +1,8 @@ + +/** + * @author Mike Conway - NIEHS Utilities for configuration of Jargon properties + * in the mid tier, perhaps destined to exist in a stand-alone jargon + * extensions package + * + */ +package org.irods.jargon.midtier.utils.configuration; \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/main/resources/META-INF/emc-metalnx-services/emc-metalnx-services-context.xml b/packaging/src/emc-metalnx-services/src/main/resources/META-INF/emc-metalnx-services/emc-metalnx-services-context.xml new file mode 100755 index 000000000..b6cd1cca1 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/main/resources/META-INF/emc-metalnx-services/emc-metalnx-services-context.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/test/java/META-INF/MANIFEST.MF b/packaging/src/emc-metalnx-services/src/test/java/META-INF/MANIFEST.MF new file mode 100644 index 000000000..254272e1c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/irods/CollectionServiceImplTest.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/irods/CollectionServiceImplTest.java new file mode 100644 index 000000000..93af34fa8 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/irods/CollectionServiceImplTest.java @@ -0,0 +1,145 @@ +package com.emc.metalnx.services.irods; + +import java.io.File; +import java.util.List; +import java.util.Properties; + +import org.irods.jargon.core.connection.IRODSAccount; +import org.irods.jargon.core.pub.CollectionAndDataObjectListAndSearchAO; +import org.irods.jargon.core.pub.DataTransferOperations; +import org.irods.jargon.core.pub.EnvironmentalInfoAO; +import org.irods.jargon.core.pub.IRODSFileSystem; +import org.irods.jargon.core.pub.SpecificQueryAO; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.irods.jargon.core.utils.MiscIRODSUtils; +import org.irods.jargon.testutils.IRODSTestSetupUtilities; +import org.irods.jargon.testutils.TestingPropertiesHelper; +import org.irods.jargon.testutils.filemanip.FileGenerator; +import org.irods.jargon.testutils.filemanip.ScratchFileUtils; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.services.interfaces.AdminServices; +import com.emc.metalnx.services.interfaces.IRODSServices; + +import junit.framework.Assert; + +public class CollectionServiceImplTest { + + private static Properties testingProperties = new Properties(); + private static TestingPropertiesHelper testingPropertiesHelper = new TestingPropertiesHelper(); + private static ScratchFileUtils scratchFileUtils = null; + public static final String IRODS_TEST_SUBDIR_PATH = "CollectionServiceImplTest"; + private static IRODSTestSetupUtilities irodsTestSetupUtilities = null; + private static IRODSFileSystem irodsFileSystem; + public static String rootCollPathInIrods; + + @AfterClass + public static void tearDownAfterClass() throws Exception { + irodsFileSystem.closeAndEatExceptions(); + } + + @After + public void afterEach() throws Exception { + irodsFileSystem.closeAndEatExceptions(); + } + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + TestingPropertiesHelper testingPropertiesLoader = new TestingPropertiesHelper(); + testingProperties = testingPropertiesLoader.getTestProperties(); + scratchFileUtils = new ScratchFileUtils(testingProperties); + scratchFileUtils.clearAndReinitializeScratchDirectory(IRODS_TEST_SUBDIR_PATH); + irodsTestSetupUtilities = new IRODSTestSetupUtilities(); + irodsTestSetupUtilities.initializeIrodsScratchDirectory(); + irodsTestSetupUtilities.initializeDirectoryForTest(IRODS_TEST_SUBDIR_PATH); + irodsFileSystem = IRODSFileSystem.instance(); + + // set up test structure + + String rootCollection = "CollectionServiceImplTestTestRoot"; + String localCollectionAbsolutePath = scratchFileUtils + .createAndReturnAbsoluteScratchPath(IRODS_TEST_SUBDIR_PATH + '/' + rootCollection); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + String irodsCollectionRootAbsolutePath = (MiscIRODSUtils.computeHomeDirectoryForIRODSAccount(irodsAccount) + '/' + + rootCollection); + rootCollPathInIrods = irodsCollectionRootAbsolutePath; + + FileGenerator.generateManyFilesAndCollectionsInParentCollectionByAbsolutePath(localCollectionAbsolutePath, + "textSearchQueryTest", 1, 2, 3, "textSearchService", ".txt", 4, 3, 2, 30); + + IRODSFileFactory irodsFileFactory = irodsFileSystem.getIRODSFileFactory(irodsAccount); + IRODSFile destFile = irodsFileFactory.instanceIRODSFile(irodsCollectionRootAbsolutePath); + destFile.deleteWithForceOption(); + destFile.mkdirs(); + DataTransferOperations dataTransferOperationsAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getDataTransferOperations(irodsAccount); + File localFile = new File(localCollectionAbsolutePath); + + dataTransferOperationsAO.putOperation(localFile, destFile, null, null); + + } + + @Test + /** + * Tests NIEHS bug 500 errors browsing to home or zone #2 + * + * @throws Exception + */ + public void testFindByNameForZoneHome() throws Exception { + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + + IRODSServicesImpl irodsServices = new IRODSServicesImpl(); + irodsServices.setIrodsAccount(irodsAccount); + irodsServices.irodsAccessObjectFactory = irodsFileSystem.getIRODSAccessObjectFactory(); + + CollectionServiceImpl collectionService = new CollectionServiceImpl(); + collectionService.irodsServices = irodsServices; + + StringBuilder sb = new StringBuilder(); + sb.append('/'); + sb.append(irodsAccount.getZone()); + sb.append("/home"); + + DataGridCollectionAndDataObject actual = collectionService.findByName(sb.toString()); + + Assert.assertNotNull("no recs returned", actual); + + } + + @Test + public void testGetSubCollectionsAndDataObjectsUnderPathThatMatchSearchTextPaginated() throws Exception { + + CollectionServiceImpl collectionService = new CollectionServiceImpl(); + IRODSServices irodsService = Mockito.mock(IRODSServices.class); + AdminServices adminServices = Mockito.mock(AdminServices.class); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + + EnvironmentalInfoAO environmentalInfoAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getEnvironmentalInfoAO(irodsAccount); + CollectionAndDataObjectListAndSearchAO listAndSearchAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getCollectionAndDataObjectListAndSearchAO(irodsAccount); + SpecificQueryAO specificQueryAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getSpecificQueryAO(irodsAccount); + + Mockito.when(irodsService.getEnvironmentalInfoAO()).thenReturn(environmentalInfoAO); + Mockito.when(irodsService.getCollectionAndDataObjectListAndSearchAO()).thenReturn(listAndSearchAO); + Mockito.when(adminServices.getSpecificQueryAO()).thenReturn(specificQueryAO); + + collectionService.setIrodsServices(irodsService); + collectionService.setAdminServices(adminServices); + List actual = collectionService + .searchCollectionAndDataObjectsByName("textSearch"); + Assert.assertTrue("no recs returned", actual.size() > 1); + + } + +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/irods/SpecQueryServiceImplTest.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/irods/SpecQueryServiceImplTest.java new file mode 100644 index 000000000..fedf1be1f --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/irods/SpecQueryServiceImplTest.java @@ -0,0 +1,365 @@ +package com.emc.metalnx.services.irods; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.irods.jargon.core.connection.IRODSAccount; +import org.irods.jargon.core.pub.CollectionAO; +import org.irods.jargon.core.pub.DataObjectAO; +import org.irods.jargon.core.pub.DataTransferOperations; +import org.irods.jargon.core.pub.EnvironmentalInfoAO; +import org.irods.jargon.core.pub.IRODSFileSystem; +import org.irods.jargon.core.pub.SpecificQueryAO; +import org.irods.jargon.core.pub.domain.AvuData; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.core.pub.io.IRODSFileFactory; +import org.irods.jargon.core.query.SpecificQueryResultSet; +import org.irods.jargon.core.utils.MiscIRODSUtils; +import org.irods.jargon.testutils.IRODSTestSetupUtilities; +import org.irods.jargon.testutils.TestingPropertiesHelper; +import org.irods.jargon.testutils.filemanip.FileGenerator; +import org.irods.jargon.testutils.filemanip.ScratchFileUtils; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +import com.emc.metalnx.core.domain.entity.DataGridFilePropertySearch; +import com.emc.metalnx.core.domain.entity.DataGridMetadataSearch; +import com.emc.metalnx.core.domain.entity.enums.DataGridSearchOperatorEnum; +import com.emc.metalnx.core.domain.entity.enums.FilePropertyField; +import com.emc.metalnx.services.interfaces.AdminServices; +import com.emc.metalnx.services.interfaces.IRODSServices; + +import junit.framework.Assert; + +public class SpecQueryServiceImplTest { + + private static Properties testingProperties = new Properties(); + private static TestingPropertiesHelper testingPropertiesHelper = new TestingPropertiesHelper(); + private static ScratchFileUtils scratchFileUtils = null; + public static final String IRODS_TEST_SUBDIR_PATH = "SpecQueryServiceImplTest"; + private static IRODSTestSetupUtilities irodsTestSetupUtilities = null; + private static IRODSFileSystem irodsFileSystem; + public static String rootCollPathInIrods; + + public static final String COLL_AVU_ATTR1 = "specQueryColl1"; + public static final String COLL_AVU_VAL1 = "specQueryCollVal1"; + + public static final String COLL_AVU_ATTR2 = "specQueryColl2"; + public static final String COLL_AVU_VAL2 = "specQueryCollVal2"; + + public static final String DATA_AVU_ATTR1 = "specQueryData1"; + public static final String DATA_AVU_VAL1 = "specQueryDataVal1"; + + public static final String DATA_AVU_ATTR2 = "specQueryData2"; + public static final String DATA_AVU_VAL2 = "specQueryDataVal2"; + + @AfterClass + public static void tearDownAfterClass() throws Exception { + irodsFileSystem.closeAndEatExceptions(); + } + + @After + public void afterEach() throws Exception { + irodsFileSystem.closeAndEatExceptions(); + } + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + TestingPropertiesHelper testingPropertiesLoader = new TestingPropertiesHelper(); + testingProperties = testingPropertiesLoader.getTestProperties(); + scratchFileUtils = new ScratchFileUtils(testingProperties); + scratchFileUtils.clearAndReinitializeScratchDirectory(IRODS_TEST_SUBDIR_PATH); + irodsTestSetupUtilities = new IRODSTestSetupUtilities(); + irodsTestSetupUtilities.initializeIrodsScratchDirectory(); + irodsTestSetupUtilities.initializeDirectoryForTest(IRODS_TEST_SUBDIR_PATH); + irodsFileSystem = IRODSFileSystem.instance(); + + // set up test structure + + String rootCollection = "SpecQueryServiceImplTestRoot"; + String localCollectionAbsolutePath = scratchFileUtils + .createAndReturnAbsoluteScratchPath(IRODS_TEST_SUBDIR_PATH + '/' + rootCollection); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + String irodsCollectionRootAbsolutePath = (MiscIRODSUtils.computeHomeDirectoryForIRODSAccount(irodsAccount) + '/' + + rootCollection); + rootCollPathInIrods = irodsCollectionRootAbsolutePath; + + FileGenerator.generateManyFilesAndCollectionsInParentCollectionByAbsolutePath(localCollectionAbsolutePath, + "specQueryTset", 1, 2, 3, "testFile", ".txt", 4, 3, 2, 30000); + + IRODSFileFactory irodsFileFactory = irodsFileSystem.getIRODSFileFactory(irodsAccount); + IRODSFile destFile = irodsFileFactory.instanceIRODSFile(irodsCollectionRootAbsolutePath); + destFile.deleteWithForceOption(); + destFile.mkdirs(); + DataTransferOperations dataTransferOperationsAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getDataTransferOperations(irodsAccount); + DataObjectAO dataObjectAO = irodsFileSystem.getIRODSAccessObjectFactory().getDataObjectAO(irodsAccount); + CollectionAO collectionAO = irodsFileSystem.getIRODSAccessObjectFactory().getCollectionAO(irodsAccount); + File localFile = new File(localCollectionAbsolutePath); + + dataTransferOperationsAO.putOperation(localFile, destFile, null, null); + + // Decorate with various AVUs to use in tests + + decorateChildren(destFile, collectionAO, dataObjectAO); + } + + public static void decorateChildren(final IRODSFile file, final CollectionAO collectionAO, + final DataObjectAO dataObjectAO) throws Exception { + IRODSFile childIrods; + int i = 0; + AvuData dataToAdd; + for (File child : file.listFiles()) { + childIrods = (IRODSFile) child; + if (childIrods.isDirectory()) { + if (i % 2 == 0) { + dataToAdd = AvuData.instance(COLL_AVU_ATTR1, COLL_AVU_VAL1, ""); + } else { + dataToAdd = AvuData.instance(COLL_AVU_ATTR2, COLL_AVU_VAL2, ""); + } + collectionAO.addAVUMetadata(childIrods.getAbsolutePath(), dataToAdd); + decorateChildren(childIrods, collectionAO, dataObjectAO); + } else { + if (i % 2 == 0) { + dataToAdd = AvuData.instance(DATA_AVU_ATTR1, DATA_AVU_VAL1, ""); + } else { + dataToAdd = AvuData.instance(DATA_AVU_ATTR2, DATA_AVU_VAL2, ""); + } + dataObjectAO.addAVUMetadata(childIrods.getAbsolutePath(), dataToAdd); + } + } + } + + @Test + public void testCountCollectionsMatchingMetadata() throws Exception { + + SpecQueryServiceImpl specQueryService = new SpecQueryServiceImpl(); + IRODSServices irodsService = Mockito.mock(IRODSServices.class); + AdminServices adminServices = Mockito.mock(AdminServices.class); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + + EnvironmentalInfoAO environmentalInfoAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getEnvironmentalInfoAO(irodsAccount); + SpecificQueryAO specificQueryAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getSpecificQueryAO(irodsAccount); + + Mockito.when(irodsService.getEnvironmentalInfoAO()).thenReturn(environmentalInfoAO); + Mockito.when(adminServices.getSpecificQueryAO()).thenReturn(specificQueryAO); + + specQueryService.setIrodsServices(irodsService); + specQueryService.setAdminServices(adminServices); + List metadataSearch = new ArrayList(); + DataGridMetadataSearch search = new DataGridMetadataSearch(COLL_AVU_ATTR1, COLL_AVU_VAL1, "", + DataGridSearchOperatorEnum.EQUAL); + metadataSearch.add(search); + int count = specQueryService.countCollectionsMatchingMetadata(metadataSearch, irodsAccount.getZone()); + Assert.assertTrue("no recs returned", count > 1); + + } + + @Test + public void testCountDataObjectsMatchingMetadata() throws Exception { + SpecQueryServiceImpl specQueryService = new SpecQueryServiceImpl(); + IRODSServices irodsService = Mockito.mock(IRODSServices.class); + AdminServices adminServices = Mockito.mock(AdminServices.class); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + + EnvironmentalInfoAO environmentalInfoAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getEnvironmentalInfoAO(irodsAccount); + SpecificQueryAO specificQueryAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getSpecificQueryAO(irodsAccount); + + Mockito.when(irodsService.getEnvironmentalInfoAO()).thenReturn(environmentalInfoAO); + Mockito.when(adminServices.getSpecificQueryAO()).thenReturn(specificQueryAO); + + specQueryService.setIrodsServices(irodsService); + specQueryService.setAdminServices(adminServices); + List metadataSearch = new ArrayList(); + DataGridMetadataSearch search = new DataGridMetadataSearch(DATA_AVU_ATTR1, DATA_AVU_VAL1, "", + DataGridSearchOperatorEnum.EQUAL); + metadataSearch.add(search); + int count = specQueryService.countDataObjectsMatchingMetadata(metadataSearch, irodsAccount.getZone()); + Assert.assertTrue("no recs returned", count > 1); + } + + @Test + public void testSearchByMetadataDataObjects() throws Exception { + SpecQueryServiceImpl specQueryService = new SpecQueryServiceImpl(); + IRODSServices irodsService = Mockito.mock(IRODSServices.class); + AdminServices adminServices = Mockito.mock(AdminServices.class); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + + EnvironmentalInfoAO environmentalInfoAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getEnvironmentalInfoAO(irodsAccount); + SpecificQueryAO specificQueryAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getSpecificQueryAO(irodsAccount); + + Mockito.when(irodsService.getEnvironmentalInfoAO()).thenReturn(environmentalInfoAO); + Mockito.when(adminServices.getSpecificQueryAO()).thenReturn(specificQueryAO); + + specQueryService.setIrodsServices(irodsService); + specQueryService.setAdminServices(adminServices); + List metadataSearch = new ArrayList(); + DataGridMetadataSearch search = new DataGridMetadataSearch(DATA_AVU_ATTR1, DATA_AVU_VAL1, "", + DataGridSearchOperatorEnum.EQUAL); + metadataSearch.add(search); + SpecificQueryResultSet result = specQueryService.searchByMetadata(metadataSearch, irodsAccount.getZone(), false, + null, 0, 0); + Assert.assertFalse("no result", result.getResults().isEmpty()); + } + + @Test + public void testSearchByMetadataCollections() throws Exception { + SpecQueryServiceImpl specQueryService = new SpecQueryServiceImpl(); + IRODSServices irodsService = Mockito.mock(IRODSServices.class); + AdminServices adminServices = Mockito.mock(AdminServices.class); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + + EnvironmentalInfoAO environmentalInfoAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getEnvironmentalInfoAO(irodsAccount); + SpecificQueryAO specificQueryAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getSpecificQueryAO(irodsAccount); + + Mockito.when(irodsService.getEnvironmentalInfoAO()).thenReturn(environmentalInfoAO); + Mockito.when(adminServices.getSpecificQueryAO()).thenReturn(specificQueryAO); + + specQueryService.setIrodsServices(irodsService); + specQueryService.setAdminServices(adminServices); + List metadataSearch = new ArrayList(); + DataGridMetadataSearch search = new DataGridMetadataSearch(COLL_AVU_ATTR1, COLL_AVU_VAL1, "", + DataGridSearchOperatorEnum.EQUAL); + metadataSearch.add(search); + SpecificQueryResultSet result = specQueryService.searchByMetadata(metadataSearch, irodsAccount.getZone(), true, + null, 0, 0); + Assert.assertFalse("no result", result.getResults().isEmpty()); + } + + @Test + public void testSearchByFilePropertiesForDataObjects() throws Exception { + SpecQueryServiceImpl specQueryService = new SpecQueryServiceImpl(); + IRODSServices irodsService = Mockito.mock(IRODSServices.class); + AdminServices adminServices = Mockito.mock(AdminServices.class); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + IRODSAccount test3Account = testingPropertiesHelper + .buildIRODSAccountFromSecondaryTestProperties(testingProperties); + + EnvironmentalInfoAO environmentalInfoAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getEnvironmentalInfoAO(irodsAccount); + SpecificQueryAO specificQueryAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getSpecificQueryAO(irodsAccount); + + Mockito.when(irodsService.getEnvironmentalInfoAO()).thenReturn(environmentalInfoAO); + Mockito.when(adminServices.getSpecificQueryAO()).thenReturn(specificQueryAO); + + specQueryService.setIrodsServices(irodsService); + specQueryService.setAdminServices(adminServices); + List filePropertiesSearch = new ArrayList<>(); + DataGridFilePropertySearch dataSearch = new DataGridFilePropertySearch(FilePropertyField.OWNER_NAME, + DataGridSearchOperatorEnum.EQUAL, test3Account.getUserName()); // use test3 because its smaller + filePropertiesSearch.add(dataSearch); + dataSearch = new DataGridFilePropertySearch(FilePropertyField.SIZE, DataGridSearchOperatorEnum.BIGGER_THAN, + "200"); + filePropertiesSearch.add(dataSearch); + SpecificQueryResultSet result = specQueryService.searchByFileProperties(filePropertiesSearch, + irodsAccount.getZone(), false, null, 0, 0); + Assert.assertNotNull("no result", result.getResults()); + } + + @Test + public void testSearchByFilePropertiesForCollections() throws Exception { + SpecQueryServiceImpl specQueryService = new SpecQueryServiceImpl(); + IRODSServices irodsService = Mockito.mock(IRODSServices.class); + AdminServices adminServices = Mockito.mock(AdminServices.class); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + IRODSAccount test3Account = testingPropertiesHelper + .buildIRODSAccountFromSecondaryTestProperties(testingProperties); + + EnvironmentalInfoAO environmentalInfoAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getEnvironmentalInfoAO(irodsAccount); + SpecificQueryAO specificQueryAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getSpecificQueryAO(irodsAccount); + + Mockito.when(irodsService.getEnvironmentalInfoAO()).thenReturn(environmentalInfoAO); + Mockito.when(adminServices.getSpecificQueryAO()).thenReturn(specificQueryAO); + + specQueryService.setIrodsServices(irodsService); + specQueryService.setAdminServices(adminServices); + List filePropertiesSearch = new ArrayList<>(); + DataGridFilePropertySearch dataSearch = new DataGridFilePropertySearch(FilePropertyField.OWNER_NAME, + DataGridSearchOperatorEnum.EQUAL, test3Account.getUserName()); + filePropertiesSearch.add(dataSearch); + SpecificQueryResultSet result = specQueryService.searchByFileProperties(filePropertiesSearch, + irodsAccount.getZone(), true, null, 0, 0); + Assert.assertFalse("no result", result.getResults().isEmpty()); + } + + @Test + public void testCountCollectionsMatchingFileProperties() throws Exception { + SpecQueryServiceImpl specQueryService = new SpecQueryServiceImpl(); + IRODSServices irodsService = Mockito.mock(IRODSServices.class); + AdminServices adminServices = Mockito.mock(AdminServices.class); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + + EnvironmentalInfoAO environmentalInfoAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getEnvironmentalInfoAO(irodsAccount); + SpecificQueryAO specificQueryAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getSpecificQueryAO(irodsAccount); + + Mockito.when(irodsService.getEnvironmentalInfoAO()).thenReturn(environmentalInfoAO); + Mockito.when(adminServices.getSpecificQueryAO()).thenReturn(specificQueryAO); + + specQueryService.setIrodsServices(irodsService); + specQueryService.setAdminServices(adminServices); + List filePropertiesSearch = new ArrayList<>(); + DataGridFilePropertySearch dataSearch = new DataGridFilePropertySearch(FilePropertyField.OWNER_NAME, + DataGridSearchOperatorEnum.EQUAL, irodsAccount.getUserName()); + filePropertiesSearch.add(dataSearch); + int count = specQueryService.countCollectionsMatchingFileProperties(filePropertiesSearch, + irodsAccount.getZone()); + Assert.assertTrue("no recs returned", count > 1); + } + + @Test + public void testCountDataObjectsMatchingFileProperties() throws Exception { + SpecQueryServiceImpl specQueryService = new SpecQueryServiceImpl(); + IRODSServices irodsService = Mockito.mock(IRODSServices.class); + AdminServices adminServices = Mockito.mock(AdminServices.class); + + IRODSAccount irodsAccount = testingPropertiesHelper.buildIRODSAccountFromTestProperties(testingProperties); + + EnvironmentalInfoAO environmentalInfoAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getEnvironmentalInfoAO(irodsAccount); + SpecificQueryAO specificQueryAO = irodsFileSystem.getIRODSAccessObjectFactory() + .getSpecificQueryAO(irodsAccount); + + Mockito.when(irodsService.getEnvironmentalInfoAO()).thenReturn(environmentalInfoAO); + Mockito.when(adminServices.getSpecificQueryAO()).thenReturn(specificQueryAO); + + specQueryService.setIrodsServices(irodsService); + specQueryService.setAdminServices(adminServices); + List filePropertiesSearch = new ArrayList<>(); + DataGridFilePropertySearch dataSearch = new DataGridFilePropertySearch(FilePropertyField.OWNER_NAME, + DataGridSearchOperatorEnum.EQUAL, irodsAccount.getUserName()); + filePropertiesSearch.add(dataSearch); + dataSearch = new DataGridFilePropertySearch(FilePropertyField.SIZE, DataGridSearchOperatorEnum.BIGGER_THAN, + "200"); + filePropertiesSearch.add(dataSearch); + int count = specQueryService.countDataObjectsMatchingFileProperties(filePropertiesSearch, + irodsAccount.getZone()); + Assert.assertTrue("no recs returned", count > 1); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/irods/utils/SpecificQueryProviderFactoryImplTest.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/irods/utils/SpecificQueryProviderFactoryImplTest.java new file mode 100644 index 000000000..5d76f7f0c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/irods/utils/SpecificQueryProviderFactoryImplTest.java @@ -0,0 +1,20 @@ +package com.emc.metalnx.services.irods.utils; + +import org.irods.jargon.core.protovalues.IcatTypeEnum; +import org.irods.jargon.core.pub.domain.ClientHints; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +public class SpecificQueryProviderFactoryImplTest { + + @Test + public void testPostgres() throws Exception { + ClientHints clientHints = Mockito.mock(ClientHints.class); + Mockito.when(clientHints.whatTypeOfIcatIsIt()).thenReturn(IcatTypeEnum.POSTGRES); + SpecificQueryProviderFactory specificQueryProviderFactory = new SpecificQueryProviderFactoryImpl(); + SpecificQueryProvider actual = specificQueryProviderFactory.instance(clientHints); + Assert.assertNotNull("null provider", actual); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/configuration/TestConfigService.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/configuration/TestConfigService.java new file mode 100755 index 000000000..62b46bf57 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/configuration/TestConfigService.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.configuration; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.configuration.ConfigServiceImpl; +import com.emc.metalnx.services.interfaces.ConfigService; +import com.emc.metalnx.services.tests.msi.MSIUtils; +import org.irods.jargon.core.exception.JargonException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.MockitoAnnotations; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.util.ReflectionTestUtils; + +import javax.annotation.PostConstruct; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.spy; + +/** + * Test for Rule Service + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestConfigService { + + public static final String DELIMITER = ","; + @InjectMocks + private ConfigService configService; + + private static String msiVersion; + private List msiList, mlxMSIList, irods41XMSIs, irods42MSIs, otherMSIList; + + @PostConstruct + public void init() { + configService = spy(ConfigServiceImpl.class); // partial mocking + + MSIUtils msiUtils = new MSIUtils(); + + msiVersion = MSIUtils.getMsiVersion(); + msiList = msiUtils.getMsiList(); + mlxMSIList = msiUtils.getMlxMSIList(); + irods41XMSIs = msiUtils.getIrods41XMSIs(); + irods42MSIs = msiUtils.getIrods420MSIs(); + otherMSIList = msiUtils.getOtherMSIs(); + } + + @Before + public void setUp() throws JargonException, DataGridException { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testMissingIrods42MSIsProperty() { + ReflectionTestUtils.setField(configService, "irods42MSIsExpected", null); + assertTrue(configService.getIrods42MSIsExpected().isEmpty()); + } + + @Test + public void testIrods42MSIsProperty() throws DataGridConnectionRefusedException { + ReflectionTestUtils.setField(configService, "irods42MSIsExpected", String.join(DELIMITER, irods42MSIs)); + List actualList = configService.getIrods42MSIsExpected(); + assertTrue(actualList.equals(irods42MSIs)); + } + + @Test + public void testMissingIrods41MSIsProperty() { + ReflectionTestUtils.setField(configService, "irods41MSIsExpected", null); + assertTrue(configService.getIrods41MSIsExpected().isEmpty()); + } + + @Test + public void testIrods41MSIsProperty() throws DataGridConnectionRefusedException { + ReflectionTestUtils.setField(configService, "irods41MSIsExpected", String.join(DELIMITER, irods41XMSIs)); + List actualList = configService.getIrods41MSIsExpected(); + assertTrue(actualList.equals(irods41XMSIs)); + } + + @Test + public void testMissingMetalnxMSIsProperty() { + ReflectionTestUtils.setField(configService, "mlxMSIsExpected", null); + assertTrue(configService.getMlxMSIsExpected().isEmpty()); + } + + @Test + public void testMetalnxMSIsProperty() throws DataGridConnectionRefusedException { + ReflectionTestUtils.setField(configService, "mlxMSIsExpected", String.join(DELIMITER, mlxMSIList)); + List actualList = configService.getMlxMSIsExpected(); + assertTrue(actualList.equals(mlxMSIList)); + } + + @Test + public void testMissingOtherMSIProperty() { + ReflectionTestUtils.setField(configService, "otherMSIsExpected", null); + assertTrue(configService.getOtherMSIsExpected().isEmpty()); + } + + @Test + public void testOtherMSIProperty() throws DataGridConnectionRefusedException { + ReflectionTestUtils.setField(configService, "otherMSIsExpected", "msi1.so,msi2.so,msi3.so"); + List msis = configService.getOtherMSIsExpected(); + assertTrue(msis.contains("msi1.so")); + assertTrue(msis.contains("msi2.so")); + assertTrue(msis.contains("msi3.so")); + } + + @Test + public void testMissingMSIAPIVersionProperty() { + ReflectionTestUtils.setField(configService, "msiAPIVersionSupported", null); + assertTrue(configService.getMsiAPIVersionSupported().isEmpty()); + } + + @Test + public void testMSIAPIVersionProperty() throws DataGridConnectionRefusedException { + ReflectionTestUtils.setField(configService, "msiAPIVersionSupported", msiVersion); + assertEquals(msiVersion, configService.getMsiAPIVersionSupported()); + } +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/fileoperations/TestFileOperationService.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/fileoperations/TestFileOperationService.java new file mode 100755 index 000000000..f58a1ef6f --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/fileoperations/TestFileOperationService.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.fileoperations; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.RuleService; +import com.emc.metalnx.services.irods.FileOperationServiceImpl; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestFileOperationService { + + public static final String RODSADMIN = "rodsadmin"; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @InjectMocks + private FileOperationService fileOperationService; + + @Mock + private RuleService ruleService; + + @Mock + private IRODSServices irodsServices; + + private DataGridUser user; + + private String path; + + @Before + public void setUp() { + path = String.format("/%s/home/%s", zone, username); + + user = new DataGridUser(); + user.setUsername(username); + user.setUserType(RODSADMIN); + + fileOperationService = spy(FileOperationServiceImpl.class); // partial mocking + + MockitoAnnotations.initMocks(this); + } + + @Test + public void testEmptyTrashRuleException() throws DataGridConnectionRefusedException, DataGridRuleException { + when(irodsServices.getDefaultStorageResource()).thenReturn("demoResc"); + doThrow(DataGridRuleException.class).when(ruleService).execEmptyTrashRule(anyString(), anyString(), anyBoolean()); + assertFalse(fileOperationService.emptyTrash(user, path)); + } + + @Test + public void testEmptyTrash() throws DataGridConnectionRefusedException, DataGridRuleException { + when(irodsServices.getDefaultStorageResource()).thenReturn("demoResc"); + doNothing().when(ruleService).execEmptyTrashRule(anyString(), anyString(), anyBoolean()); + assertTrue(fileOperationService.emptyTrash(user, path)); + } + + @Test + public void testInvocationsOfEmptyTrashRule() throws DataGridConnectionRefusedException, DataGridRuleException { + when(irodsServices.getDefaultStorageResource()).thenReturn("demoResc"); + doNothing().when(ruleService).execEmptyTrashRule(anyString(), anyString(), anyBoolean()); + fileOperationService.emptyTrash(user, path); + verify(ruleService, times(1)).execEmptyTrashRule(anyString(), anyString(), anyBoolean()); + } + + @Test + public void testNullPath() throws DataGridConnectionRefusedException { + assertFalse(fileOperationService.emptyTrash(user, null)); + } + + @Test + public void testEmptyPath() throws DataGridConnectionRefusedException { + assertFalse(fileOperationService.emptyTrash(user, "")); + } + + @Test + public void testNullUser() throws DataGridConnectionRefusedException { + assertFalse(fileOperationService.emptyTrash(null, path)); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/irodsservices/TestIRodsVersion.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/irodsservices/TestIRodsVersion.java new file mode 100755 index 000000000..716e3da79 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/irodsservices/TestIRodsVersion.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.irodsservices; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestIRodsVersion { + + @Autowired + private IRODSServices irodsServices; + + @Test + public void testFindIRodsVersion() throws DataGridConnectionRefusedException { + String version = irodsServices.findIRodsVersion(); + Assert.assertNotNull(version); + Assert.assertFalse(version.isEmpty()); + Assert.assertTrue(version.matches("^\\d+.\\d+.\\d+$")); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/MetadataUtils.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/MetadataUtils.java new file mode 100755 index 000000000..ccc6718cd --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/MetadataUtils.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import org.junit.Assert; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertTrue; + +/** + * Utils class for Metadata tests. + */ +public class MetadataUtils { + static void assertDataGridMetadataInPath(String path, List expectedMetadataList, + List actualMetadataList) + throws DataGridConnectionRefusedException { + + Assert.assertEquals(expectedMetadataList.size(), actualMetadataList.size()); + + for (DataGridMetadata actualMetadata: actualMetadataList) { + assertTrue(expectedMetadataList.contains(actualMetadata)); + } + } + + static List createRandomMetadata(int numberOfMetadataTags) { + List metadataList = new ArrayList<>(); + + for(int i = 0; i < numberOfMetadataTags; i++) { + String attribute = "attr" + i; + String value = "val" + i; + String unit = "unit" + i; + metadataList.add(new DataGridMetadata(attribute, value, unit)); + } + + return metadataList; + } + + static List createRandomMetadataAsString(int numberOfMetadataTags) { + List metadataList = new ArrayList<>(); + + for(int i = 0; i < numberOfMetadataTags; i++) { + String attribute = "attr" + i; + String value = "val" + i; + String unit = "unit" + i; + metadataList.add(attribute + " " + value + " " + unit); + } + + return metadataList; + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataListToColls.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataListToColls.java new file mode 100755 index 000000000..6ecaccd06 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataListToColls.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test metadata service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestAddDataGridMetadataListToColls { + private static final String BASE_COLL_NAME = "test-coll-transfer-"; + private static final int NUMBER_OF_COLLS = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private MetadataService metadataService; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + private String parentPath, path; + + private List expectedMetadataList; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + path = String.format("%s/test-metadata-transfer", parentPath); + + fos.deleteCollection(path, true); + cs.createCollection(new DataGridCollectionAndDataObject(path, parentPath, true)); + + expectedMetadataList = new ArrayList<>(); + expectedMetadataList.add(new DataGridMetadata("attr1", "val1", "unit1")); + expectedMetadataList.add(new DataGridMetadata("attr2", "val2", "unit2")); + expectedMetadataList.add(new DataGridMetadata("attr3", "val3", "unit3")); + + for(int i = 0; i < NUMBER_OF_COLLS; i++) { + String collname = BASE_COLL_NAME + i; + String collPath = String.format("%s/%s", path, collname); + cs.createCollection(new DataGridCollectionAndDataObject(collPath, path, true)); + + metadataService.addMetadataToPath(collPath, expectedMetadataList); + } + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(path, true); + } + + @Test + public void testAddMetadataToColls() throws DataGridConnectionRefusedException { + for (int i = 0; i < NUMBER_OF_COLLS; i++) { + String collPath = String.format("%s/%s", path, BASE_COLL_NAME + i); + List actualMetadataList = metadataService.findMetadataValuesByPath(collPath); + MetadataUtils.assertDataGridMetadataInPath(collPath, expectedMetadataList, actualMetadataList); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataListToObjs.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataListToObjs.java new file mode 100755 index 000000000..e55d46b5a --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataListToObjs.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test metadata service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestAddDataGridMetadataListToObjs { + private static final String BASE_FILE_NAME = "test-coll-transfer-"; + private static final String RESOURCE = "demoResc"; + private static final int NUMBER_OF_FILES = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private MetadataService metadataService; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + private String parentPath, path; + + private List expectedMetadataList; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + path = String.format("%s/test-metadata-transfer", parentPath); + + fos.deleteCollection(path, true); + cs.createCollection(new DataGridCollectionAndDataObject(path, parentPath, true)); + + expectedMetadataList = new ArrayList<>(); + expectedMetadataList.add(new DataGridMetadata("attr1", "val1", "unit1")); + expectedMetadataList.add(new DataGridMetadata("attr2", "val2", "unit2")); + expectedMetadataList.add(new DataGridMetadata("attr3", "val3", "unit3")); + + MockMultipartFile file; + for(int i = 0; i < NUMBER_OF_FILES; i++) { + String filename = BASE_FILE_NAME + i; + String filepath = String.format("%s/%s", path, filename); + + file = new MockMultipartFile(filename, "Hello World Transfer".getBytes()); + us.upload(file, path, false, false, "", RESOURCE, false); + + metadataService.addMetadataToPath(filepath, expectedMetadataList); + } + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(path, true); + } + + @Test + public void testAddMetadataToColls() throws DataGridConnectionRefusedException { + for (int i = 0; i < NUMBER_OF_FILES; i++) { + String filepath = String.format("%s/%s", path, BASE_FILE_NAME + i); + + List actualMetadataList = metadataService.findMetadataValuesByPath(filepath); + MetadataUtils.assertDataGridMetadataInPath(filepath, expectedMetadataList, actualMetadataList); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataToColls.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataToColls.java new file mode 100755 index 000000000..c89c4ba83 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataToColls.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.List; + +/** + * Test metadata service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestAddDataGridMetadataToColls { + private static final String BASE_COLL_NAME = "test-coll-transfer-"; + private static final int NUMBER_OF_COLLS = 3; + private static final int NUMBER_OF_METADATA_TAGS = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private MetadataService metadataService; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + private String parentPath, path; + + private List expectedMetadataList; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + path = String.format("%s/test-metadata-transfer", parentPath); + + fos.deleteCollection(path, true); + cs.createCollection(new DataGridCollectionAndDataObject(path, parentPath, true)); + + expectedMetadataList = MetadataUtils.createRandomMetadata(NUMBER_OF_METADATA_TAGS); + + for(int i = 0; i < NUMBER_OF_COLLS; i++) { + String collPath = String.format("%s/%s", path, BASE_COLL_NAME + i); + cs.createCollection(new DataGridCollectionAndDataObject(collPath, path, true)); + + for(DataGridMetadata metadata: expectedMetadataList) { + metadataService.addMetadataToPath(collPath, metadata); + } + } + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(path, true); + } + + @Test + public void testAddMetadataToColls() throws DataGridConnectionRefusedException { + for (int i = 0; i < NUMBER_OF_COLLS; i++) { + String collPath = String.format("%s/%s", path, BASE_COLL_NAME + i); + List actualMetadataList = metadataService.findMetadataValuesByPath(collPath); + MetadataUtils.assertDataGridMetadataInPath(collPath, expectedMetadataList, actualMetadataList); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataToObjs.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataToObjs.java new file mode 100755 index 000000000..7fed44669 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddDataGridMetadataToObjs.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.List; + +/** + * Test metadata service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestAddDataGridMetadataToObjs { + private static final String BASE_FILE_NAME = "test-coll-transfer-"; + private static final String RESOURCE = "demoResc"; + private static final int NUMBER_OF_FILES = 3; + private static final int NUMBER_OF_METADATA_TAGS = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private MetadataService metadataService; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + private String parentPath, path; + + private List expectedMetadataList; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + path = String.format("%s/test-metadata-transfer", parentPath); + + fos.deleteCollection(path, true); + cs.createCollection(new DataGridCollectionAndDataObject(path, parentPath, true)); + + expectedMetadataList = MetadataUtils.createRandomMetadata(NUMBER_OF_METADATA_TAGS); + + MockMultipartFile file; + for(int i = 0; i < NUMBER_OF_FILES; i++) { + String filename = BASE_FILE_NAME + i; + String filepath = String.format("%s/%s", path, filename); + + file = new MockMultipartFile(filename, "Hello World Transfer".getBytes()); + us.upload(file, path, false, false, "", RESOURCE, false); + + for(DataGridMetadata metadata: expectedMetadataList) { + metadataService.addMetadataToPath(filepath, metadata); + } + } + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(path, true); + } + + @Test + public void testAddMetadataToColls() throws DataGridConnectionRefusedException { + for (int i = 0; i < NUMBER_OF_FILES; i++) { + String filepath = String.format("%s/%s", path, BASE_FILE_NAME + i); + + List actualMetadataList = metadataService.findMetadataValuesByPath(filepath); + MetadataUtils.assertDataGridMetadataInPath(filepath, expectedMetadataList, actualMetadataList); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddMetadataToColls.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddMetadataToColls.java new file mode 100755 index 000000000..578396686 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddMetadataToColls.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.List; + +import static junit.framework.Assert.assertTrue; + +/** + * Test metadata service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestAddMetadataToColls { + private static final String BASE_COLL_NAME = "test-coll-transfer-"; + private static final int NUMBER_OF_COLLS = 3; + private static final int NUMBER_OF_METADATA_TAGS = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private MetadataService metadataService; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + private String parentPath, path; + + private List expectedMetadataList; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + path = String.format("%s/test-metadata-transfer", parentPath); + + fos.deleteCollection(path, true); + cs.createCollection(new DataGridCollectionAndDataObject(path, parentPath, true)); + + expectedMetadataList = MetadataUtils.createRandomMetadataAsString(NUMBER_OF_METADATA_TAGS); + + for(int i = 0; i < NUMBER_OF_COLLS; i++) { + String collname = BASE_COLL_NAME + i; + String collPath = String.format("%s/%s", path, collname); + cs.createCollection(new DataGridCollectionAndDataObject(collPath, path, true)); + + for(String metadataStr: expectedMetadataList) { + String[] metadata = metadataStr.split(" "); + String attr = metadata[0], val = metadata[1], unit = metadata[2]; + metadataService.addMetadataToPath(collPath, attr, val, unit); + } + } + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(path, true); + } + + @Test + public void testAddMetadataToColls() throws DataGridConnectionRefusedException { + for (int i = 0; i < NUMBER_OF_COLLS; i++) { + String collname = BASE_COLL_NAME + i; + String collPath = String.format("%s/%s", path, collname); + assertMetadataInPath(collPath); + } + } + + private void assertMetadataInPath(String path) throws DataGridConnectionRefusedException { + List actualMetadataList = metadataService.findMetadataValuesByPath(path); + + Assert.assertEquals(expectedMetadataList.size(), actualMetadataList.size()); + + for (DataGridMetadata m: actualMetadataList) { + String metadataStr = m.getAttribute() + " " + m.getValue() + " " + m.getUnit(); + assertTrue(expectedMetadataList.contains(metadataStr)); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddMetadataToObjs.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddMetadataToObjs.java new file mode 100755 index 000000000..54dcfcd65 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestAddMetadataToObjs.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.List; + +import static junit.framework.Assert.assertTrue; + +/** + * Test metadata service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestAddMetadataToObjs { + private static final String BASE_FILE_NAME = "test-file-transfer-"; + private static final String RESOURCE = "demoResc"; + private static final int NUMBER_OF_FILES = 3; + private static final int NUMBER_OF_METADATA_TAGS = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private MetadataService metadataService; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + private String parentPath, srcPath, dstPath; + + private List expectedMetadataList; + + private long time; + + @Before + public void setUp() throws DataGridException { + time = System.currentTimeMillis(); + + parentPath = String.format("/%s/home/%s", zone, username); + srcPath = String.format("%s/test-metadata-transfer-%d", parentPath, time); + dstPath = String.format("%s/dst-test-metadata-transfer-%d", parentPath, time); + + fos.deleteCollection(srcPath, true); + fos.deleteCollection(dstPath, true); + + cs.createCollection(new DataGridCollectionAndDataObject(srcPath, parentPath, true)); + cs.createCollection(new DataGridCollectionAndDataObject(dstPath, parentPath, true)); + + expectedMetadataList = MetadataUtils.createRandomMetadataAsString(NUMBER_OF_METADATA_TAGS); + + MockMultipartFile file; + for(int i = 0; i < NUMBER_OF_FILES; i++) { + String filename = BASE_FILE_NAME + i; + String fileSrcPath = String.format("%s/%s", srcPath, filename); + + file = new MockMultipartFile(filename, "Hello World Transfer".getBytes()); + fos.deleteDataObject(fileSrcPath, true); + us.upload(file, srcPath, false, false, "", RESOURCE, false); + + for(String metadataStr: expectedMetadataList) { + String[] metadata = metadataStr.split(" "); + String attr = metadata[0], val = metadata[1], unit = metadata[2]; + metadataService.addMetadataToPath(fileSrcPath, attr, val, unit); + } + } + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(srcPath, true); + fos.deleteCollection(dstPath, true); + } + + @Test + public void testAddMetadataToObj() throws DataGridConnectionRefusedException { + for (int i = 0; i < NUMBER_OF_FILES; i++) { + String filename = BASE_FILE_NAME + i; + String filePath = String.format("%s/%s", srcPath, filename); + assertMetadataInPath(filePath); + } + } + + private void assertMetadataInPath(String path) throws DataGridConnectionRefusedException { + List actualMetadataList = metadataService.findMetadataValuesByPath(path); + + Assert.assertEquals(expectedMetadataList.size(), actualMetadataList.size()); + + for (DataGridMetadata m: actualMetadataList) { + String metadataStr = m.getAttribute() + " " + m.getValue() + " " + m.getUnit(); + assertTrue(expectedMetadataList.contains(metadataStr)); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestCopyCollWithMetadata.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestCopyCollWithMetadata.java new file mode 100755 index 000000000..044e6c3c4 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestCopyCollWithMetadata.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.MetadataService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Test copying collections with metadata. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCopyCollWithMetadata { + private static final String BASE_COLL_NAME = "test-coll-transfer-"; + private static final int NUMBER_OF_COLLS = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private MetadataService metadataService; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + private String parentPath, srcPath, dstPath; + + private List expectedMetadataList; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + srcPath = String.format("%s/test-metadata-transfer", parentPath); + dstPath = String.format("%s/dst-test-metadata-transfer", parentPath); + + fos.deleteCollection(srcPath, true); + fos.deleteCollection(dstPath, true); + + cs.createCollection(new DataGridCollectionAndDataObject(srcPath, parentPath, true)); + cs.createCollection(new DataGridCollectionAndDataObject(dstPath, parentPath, true)); + + expectedMetadataList = new ArrayList<>(); + expectedMetadataList.add("attr1 val1 unit1"); + expectedMetadataList.add("attr2 val2 unit2"); + expectedMetadataList.add("attr3 val3 unit3"); + + for(int i = 0; i < NUMBER_OF_COLLS; i++) { + String collname = BASE_COLL_NAME + i; + String collSrcPath = String.format("%s/%s", srcPath, collname); + cs.createCollection(new DataGridCollectionAndDataObject(collSrcPath, srcPath, true)); + + for(String s: expectedMetadataList) { + String[] metadata = s.split(" "); + String attr = metadata[0], val = metadata[1], unit = metadata[2]; + metadataService.addMetadataToPath(collSrcPath, attr, val, unit); + } + } + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(srcPath, true); + fos.deleteCollection(dstPath, true); + } + + @Test + public void testCopyOneFileWithMetadata() throws DataGridException { + String collname = BASE_COLL_NAME + "0"; + String collSrcPath = String.format("%s/%s", srcPath, collname); + fos.copy(collSrcPath, dstPath, true); + String collDstPath = String.format("%s/%s", dstPath, collname); + assertMetadataInPath(collDstPath); + } + + @Test + public void testCopyOneFileWithoutMetadata() throws DataGridException { + String collname = BASE_COLL_NAME + "0"; + String collSrcPath = String.format("%s/%s", srcPath, collname); + fos.copy(collSrcPath, dstPath, false); + String collDstPath = String.format("%s/%s", dstPath, collname); + assertTrue(metadataService.findMetadataValuesByPath(collDstPath).isEmpty()); + } + + @Test + public void testCopySeveralFilesWithMetadata() throws DataGridException { + for(int i = 0; i < NUMBER_OF_COLLS; i++) { + String collname = BASE_COLL_NAME + i; + String collSrcPath = String.format("%s/%s", srcPath, collname); + fos.copy(collSrcPath, dstPath, true); + String collDstPath = String.format("%s/%s", dstPath, collname); + assertMetadataInPath(collDstPath); + } + } + + @Test + public void testCopySeveralFilesWithoutMetadata() throws DataGridException { + for(int i = 0; i < NUMBER_OF_COLLS; i++) { + String collname = BASE_COLL_NAME + i; + String collSrcPath = String.format("%s/%s", srcPath, collname); + fos.copy(collSrcPath, dstPath, false); + String collDstPath = String.format("%s/%s", dstPath, collname); + assertTrue(metadataService.findMetadataValuesByPath(collDstPath).isEmpty()); + } + } + + private void assertMetadataInPath(String path) throws DataGridConnectionRefusedException { + List actualMetadataList = metadataService.findMetadataValuesByPath(path); + + assertEquals(expectedMetadataList.size(), actualMetadataList.size()); + + for (DataGridMetadata m: actualMetadataList) { + String metadataStr = m.getAttribute() + " " + m.getValue() + " " + m.getUnit(); + assertTrue(expectedMetadataList.contains(metadataStr)); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestCopyObjWithMetadata.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestCopyObjWithMetadata.java new file mode 100755 index 000000000..4df4ae977 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestCopyObjWithMetadata.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertTrue; + +/** + * Test copying data objects with metadata. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCopyObjWithMetadata { + private static final String BASE_FILE_NAME = "test-file-transfer-"; + private static final String RESOURCE = "demoResc"; + private static final int NUMBER_OF_FILES = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private MetadataService metadataService; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + private String parentPath, srcPath, dstPath; + + private List expectedMetadataList; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + srcPath = String.format("%s/test-metadata-transfer", parentPath); + dstPath = String.format("%s/dst-test-metadata-transfer", parentPath); + + fos.deleteCollection(srcPath, true); + fos.deleteCollection(dstPath, true); + + cs.createCollection(new DataGridCollectionAndDataObject(srcPath, parentPath, true)); + cs.createCollection(new DataGridCollectionAndDataObject(dstPath, parentPath, true)); + + String content = "Hello World Transfer"; + + expectedMetadataList = new ArrayList<>(); + expectedMetadataList.add("attr1 val1 unit1"); + expectedMetadataList.add("attr2 val2 unit2"); + expectedMetadataList.add("attr3 val3 unit3"); + + MockMultipartFile file; + for(int i = 0; i < NUMBER_OF_FILES; i++) { + String filename = BASE_FILE_NAME + i; + String fileSrcPath = String.format("%s/%s", srcPath, filename); + + file = new MockMultipartFile(filename, content.getBytes()); + us.upload(file, srcPath, false, false, "", RESOURCE, false); + + for(String s: expectedMetadataList) { + String[] metadata = s.split(" "); + String attr = metadata[0], val = metadata[1], unit = metadata[2]; + metadataService.addMetadataToPath(fileSrcPath, attr, val, unit); + } + } + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(srcPath, true); + fos.deleteCollection(dstPath, true); + } + + @Test + public void testCopyOneFileWithMetadata() throws DataGridException { + String filename = BASE_FILE_NAME + "0"; + String fileSrcPath = String.format("%s/%s", srcPath, filename); + fos.copy(fileSrcPath, dstPath, true); + String fileDstPath = String.format("%s/%s", dstPath, filename); + assertMetadataInPath(fileDstPath); + } + + @Test + public void testCopyOneFileWithoutMetadata() throws DataGridException { + String filename = BASE_FILE_NAME + "0"; + String fileSrcPath = String.format("%s/%s", srcPath, filename); + fos.copy(fileSrcPath, dstPath, false); + String fileDstPath = String.format("%s/%s", dstPath, filename); + assertTrue(metadataService.findMetadataValuesByPath(fileDstPath).isEmpty()); + } + + @Test + public void testCopySeveralFilesWithMetadata() throws DataGridException { + for(int i = 0; i < NUMBER_OF_FILES; i++) { + String filename = BASE_FILE_NAME + i; + String fileSrcPath = String.format("%s/%s", srcPath, filename); + fos.copy(fileSrcPath, dstPath, true); + String fileDstPath = String.format("%s/%s", dstPath, filename); + assertMetadataInPath(fileDstPath); + } + } + + @Test + public void testCopySeveralFilesWithoutMetadata() throws DataGridException { + for(int i = 0; i < NUMBER_OF_FILES; i++) { + String filename = BASE_FILE_NAME + i; + String fileSrcPath = String.format("%s/%s", srcPath, filename); + fos.copy(fileSrcPath, dstPath, false); + String fileDstPath = String.format("%s/%s", dstPath, filename); + assertTrue(metadataService.findMetadataValuesByPath(fileDstPath).isEmpty()); + } + } + + private void assertMetadataInPath(String path) throws DataGridConnectionRefusedException { + List actualMetadataList = metadataService.findMetadataValuesByPath(path); + + Assert.assertEquals(expectedMetadataList.size(), actualMetadataList.size()); + + for (DataGridMetadata m: actualMetadataList) { + String metadataStr = m.getAttribute() + " " + m.getValue() + " " + m.getUnit(); + assertTrue(expectedMetadataList.contains(metadataStr)); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestMetadataCase.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestMetadataCase.java new file mode 100755 index 000000000..e1b47d90d --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/metadata/TestMetadataCase.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.metadata; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadataSearch; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.entity.enums.DataGridSearchOperatorEnum; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.*; + +/** + * Test metadata service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestMetadataCase { + private static final String BASE_FILE_NAME = "test-file-"; + private static final String RESOURCE = "demoResc"; + private static final int NUMBER_OF_FILES = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private MetadataService metadataService; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + private String targetPath, parentPath, attr, val, unit; + + private List objs; + + private List search; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + targetPath = String.format("%s/test-metadata-search", parentPath); + + fos.deleteCollection(targetPath, true); + cs.createCollection(new DataGridCollectionAndDataObject(targetPath, parentPath, true)); + + for(int i = 0; i < NUMBER_OF_FILES; i++) { + String filename = BASE_FILE_NAME + i + ".txt"; + MockMultipartFile file = new MockMultipartFile(filename, "Hello World".getBytes()); + us.upload(file, targetPath, false, false, "", RESOURCE, false); + + String filepath = String.format("%s/%s", targetPath, filename); + + metadataService.addMetadataToPath(filepath, "TEST", "TEST", "TEST"); + metadataService.addMetadataToPath(filepath, "test", "test", "test"); + metadataService.addMetadataToPath(filepath, "TeSt", "tEsT", "teST"); + } + + attr = "test"; + val = "TEST"; + unit = "TEst"; + + search = new ArrayList<>(); + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(targetPath, true); + } + + @Test + public void testCaseInsensitiveMetadataSearchEqual() throws DataGridConnectionRefusedException { + search.add(new DataGridMetadataSearch(attr, val, unit, DataGridSearchOperatorEnum.EQUAL)); + assertMetadataSearch(3, 1); + } + + @Test + public void testCaseInsensitiveMetadataSearchContains() throws DataGridConnectionRefusedException { + search.add(new DataGridMetadataSearch(attr, val, unit, DataGridSearchOperatorEnum.LIKE)); + assertMetadataSearch(3, 1); + } + + private void assertMetadataSearch(int expectedNumOfFiles, int expectedNumOfMatchesByFile) + throws DataGridConnectionRefusedException { + objs = metadataService.findByMetadata(search, new DataGridPageContext(), 1, 100); + + assertTrue(objs.size() >= expectedNumOfFiles); + + for(DataGridCollectionAndDataObject obj: objs) { + assertTrue(obj.isVisibleToCurrentUser()); + assertFalse(obj.isCollection()); + assertEquals(expectedNumOfMatchesByFile, obj.getNumberOfMatches()); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/msi/MSIUtils.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/msi/MSIUtils.java new file mode 100755 index 000000000..0c43f708e --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/msi/MSIUtils.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.msi; + +import java.util.ArrayList; +import java.util.List; + +/** + * Microservices utils for testing. + */ +public class MSIUtils { + + private static String msiVersion; + private List msiList, mlxMSIList, irods41XMSIs, irods420MSIs, otherMSIList; + + public MSIUtils() { + msiVersion = "1.1.0"; + + msiList = new ArrayList<>(); + mlxMSIList = new ArrayList<>(); + irods41XMSIs = new ArrayList<>(); + irods420MSIs = new ArrayList<>(); + otherMSIList = new ArrayList<>(); + + mlxMSIList.add("libmsiget_illumina_meta.so"); + mlxMSIList.add("libmsiobjget_microservices.so"); + mlxMSIList.add("libmsiobjget_version.so"); + mlxMSIList.add("libmsiobjjpeg_extract.so"); + mlxMSIList.add("libmsiobjput_mdbam.so"); + mlxMSIList.add("libmsiobjput_mdbam.so"); + mlxMSIList.add("libmsiobjput_mdmanifest.so"); + mlxMSIList.add("libmsiobjput_mdvcf.so"); + mlxMSIList.add("libmsiobjput_populate.so"); + + irods420MSIs.add("libmsisync_to_archive.so"); + irods420MSIs.add("libmsi_update_unixfilesystem_resource_free_space.so"); + + irods41XMSIs.addAll(irods420MSIs); + irods41XMSIs.add("libmsiobjput_http.so"); + irods41XMSIs.add("libmsiobjput_irods.so"); + irods41XMSIs.add("libmsiobjget_irods.so"); + irods41XMSIs.add("libmsiobjget_http.so"); + irods41XMSIs.add("libmsiobjput_slink.so"); + irods41XMSIs.add("libmsiobjget_slink.so"); + + otherMSIList.add("libmsitest_other1.so"); + otherMSIList.add("libmsitest_other2.so"); + + msiList.addAll(mlxMSIList); + msiList.addAll(irods41XMSIs); + msiList.addAll(otherMSIList); + } + + public static String getMsiVersion() { + return msiVersion; + } + + public List getMsiList() { + return msiList; + } + + public List getMlxMSIList() { + return mlxMSIList; + } + + public List getIrods41XMSIs() { + return irods41XMSIs; + } + + public List getIrods420MSIs() { + return irods420MSIs; + } + + public List getOtherMSIs() { return otherMSIList; } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/msi/TestMSIService.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/msi/TestMSIService.java new file mode 100755 index 000000000..2965b79ec --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/msi/TestMSIService.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.msi; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; +import com.emc.metalnx.services.interfaces.*; +import com.emc.metalnx.services.irods.MSIServiceImpl; +import org.irods.jargon.core.exception.JargonException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyListOf; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +/** + * Test for Rule Service + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestMSIService { + + @InjectMocks + private MSIService msiService; + + @Mock + private ResourceService mockResourceService; + + @Mock + private RuleService mockRuleService; + + @Mock + private IRODSServices irodsServices; + + @Mock + private ConfigService mockConfigService; + + private static String msiVersion; + private List msiList, mlxMSIList, irods41XMSIs, irods42MSIs, otherMSIList; + + private List servers; + + @PostConstruct + public void init() { + msiService = spy(MSIServiceImpl.class); // partial mocking + + servers = new ArrayList<>(); + + MSIUtils msiUtils = new MSIUtils(); + + msiVersion = MSIUtils.getMsiVersion(); + msiList = msiUtils.getMsiList(); + mlxMSIList = msiUtils.getMlxMSIList(); + irods41XMSIs = msiUtils.getIrods41XMSIs(); + irods42MSIs = msiUtils.getIrods420MSIs(); + otherMSIList = msiUtils.getOtherMSIs(); + } + + @Before + public void setUp() throws JargonException, DataGridException { + MockitoAnnotations.initMocks(this); + + DataGridServer s1 = new DataGridServer(); + s1.setHostname("server1.test.com"); + s1.setMSIVersion(msiVersion); + s1.setIp("192.168.0.1"); + s1.setResources(new ArrayList<>()); + + DataGridServer s2 = new DataGridServer(); + s2.setHostname("server2.test.com"); + s2.setMSIVersion(msiVersion); + s2.setIp("192.168.0.2"); + s2.setResources(new ArrayList<>()); + + servers.add(s1); + servers.add(s2); + + when(mockResourceService.getAllResourceServers(anyListOf(DataGridResource.class))).thenReturn(servers); + when(mockRuleService.execGetVersionRule(anyString())).thenReturn(msiVersion); + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(msiList); + + when(mockConfigService.getMlxMSIsExpected()).thenReturn(mlxMSIList); + when(mockConfigService.getIrods41MSIsExpected()).thenReturn(irods41XMSIs); + when(mockConfigService.getIrods42MSIsExpected()).thenReturn(irods42MSIs); + when(mockConfigService.getOtherMSIsExpected()).thenReturn(otherMSIList); + } + + @Test + public void testEmptyHost() throws DataGridConnectionRefusedException { + assertNull(msiService.getMSIsInstalled("")); + } + + @Test + public void testNullHost() throws DataGridConnectionRefusedException { + assertNull(msiService.getMSIsInstalled(null)); + } + + @Test + public void testOtherMSIInstalledFor420() throws DataGridConnectionRefusedException, DataGridRuleException { + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(otherMSIList); + when(irodsServices.isAtLeastIrods420()).thenReturn(true); + DataGridServer server = msiService.getMSIsInstalled("server1.test.com"); + + assertTrue(server.isThereAnyMSI()); + assertMap(otherMSIList, server.getOtherMSIs()); + } + + @Test + public void testOtherMSIInstalledFor41() throws DataGridConnectionRefusedException, DataGridRuleException { + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(otherMSIList); + when(irodsServices.isAtLeastIrods420()).thenReturn(false); + DataGridServer server = msiService.getMSIsInstalled("server1.test.com"); + + assertTrue(server.isThereAnyMSI()); + assertMap(otherMSIList, server.getOtherMSIs()); + } + + @Test + public void testNoOtherMSIInstalledFor41() throws DataGridConnectionRefusedException, DataGridRuleException { + when(irodsServices.isAtLeastIrods420()).thenReturn(false); + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(new ArrayList<>()); + when(mockConfigService.getOtherMSIsExpected()).thenReturn(new ArrayList<>()); + DataGridServer server = msiService.getMSIsInstalled("server1.test.com"); + + assertFalse(server.isThereAnyMSI()); + assertTrue(server.getOtherMSIs().isEmpty()); + } + + @Test + public void testNoOtherMSIListed() throws DataGridConnectionRefusedException, DataGridRuleException { + String testMSI = "libmsitest_installed.so"; + + List otherMSIListWithEmptyString = new ArrayList<>(); + otherMSIListWithEmptyString.add(""); + otherMSIListWithEmptyString.add(testMSI); + + when(irodsServices.isAtLeastIrods420()).thenReturn(false); + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(otherMSIListWithEmptyString); + when(mockConfigService.getOtherMSIsExpected()).thenReturn(otherMSIListWithEmptyString); + DataGridServer server = msiService.getMSIsInstalled("server1.test.com"); + + assertTrue(server.isThereAnyMSI()); + assertFalse(server.getOtherMSIs().isEmpty()); + assertEquals(1, server.getOtherMSIs().size()); + assertTrue(server.getOtherMSIs().containsKey(testMSI)); + } + + @Test + public void testGetMSIInstalledFor420Server() throws DataGridConnectionRefusedException, DataGridRuleException { + List msis = new ArrayList<>(mlxMSIList); + msis.addAll(irods42MSIs); + msis.addAll(otherMSIList); + + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(msis); + when(irodsServices.isAtLeastIrods420()).thenReturn(true); + DataGridServer server = msiService.getMSIsInstalled("server1.test.com"); + + assertTrue(server.isThereAnyMSI()); + assertMap(mlxMSIList, server.getMetalnxMSIs()); + assertMap(irods42MSIs, server.getIRODSMSIs()); + assertMap(otherMSIList, server.getOtherMSIs()); + } + + @Test + public void testGetMSIInstalledFor41XServer() throws DataGridConnectionRefusedException, DataGridRuleException { + List msis = new ArrayList<>(mlxMSIList); + msis.addAll(irods41XMSIs); + msis.addAll(otherMSIList); + + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(msis); + when(irodsServices.isAtLeastIrods420()).thenReturn(false); + DataGridServer server = msiService.getMSIsInstalled("server1.test.com"); + + assertTrue(server.isThereAnyMSI()); + assertMap(mlxMSIList, server.getMetalnxMSIs()); + assertMap(irods41XMSIs, server.getIRODSMSIs()); + assertMap(otherMSIList, server.getOtherMSIs()); + } + + public void assertMap(List msiList, Map map) { + for(String msi: msiList) { + assertTrue(map.containsKey(msi)); + assertTrue(map.get(msi)); + } + } +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/permissions/TestMostRestrictivePermission.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/permissions/TestMostRestrictivePermission.java new file mode 100755 index 000000000..e7456f74f --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/permissions/TestMostRestrictivePermission.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.permissions; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.enums.DataGridPermType; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.PermissionsService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static junit.framework.Assert.assertEquals; + +/** + * Test metadata service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestMostRestrictivePermission { + private static final String BASE_FILE_NAME = "test-file-"; + private static final String RESOURCE = "demoResc"; + private static final int NUMBER_OF_FILES = 100; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + @Autowired + private PermissionsService permissionsService; + + private String targetPath, parentPath; + + private String[] files = new String[NUMBER_OF_FILES]; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + targetPath = String.format("%s/test-perm-%d", parentPath, System.currentTimeMillis()); + + fos.deleteCollection(targetPath, true); + cs.createCollection(new DataGridCollectionAndDataObject(targetPath, parentPath, true)); + } + + @After + public void tearDown() throws DataGridException { + fos.deleteCollection(targetPath, true); + } + + @Test + public void testFindMostRestrictivePermission() throws DataGridException { + uploadTestFiles(100); + assertEquals(DataGridPermType.OWN, permissionsService.findMostRestrictivePermission(files)); + } + + @Test + public void testFindMostRestrictivePermissionForSingleFile() throws DataGridException { + uploadTestFiles(1); + for(int i = 0; i < 100; i++) { + assertEquals(DataGridPermType.OWN, permissionsService.findMostRestrictivePermission(files[0])); + } + } + + private void uploadTestFiles(int numberOfFiles) throws DataGridException { + for(int i = 0; i < numberOfFiles; i++) { + String filename = BASE_FILE_NAME + i; + MockMultipartFile file = new MockMultipartFile(filename, "Hello World".getBytes()); + us.upload(file, targetPath, false, false, "", RESOURCE, false); + files[i] = String.format("%s/%s", targetPath, filename); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/permissions/TestReadPermissionOnFiles.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/permissions/TestReadPermissionOnFiles.java new file mode 100755 index 000000000..066291c61 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/permissions/TestReadPermissionOnFiles.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.permissions; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.enums.DataGridPermType; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.PermissionsService; +import com.emc.metalnx.services.interfaces.UploadService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static junit.framework.Assert.assertTrue; + +/** + * Test metadata service. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestReadPermissionOnFiles { + private static final String BASE_FILE_NAME = "test-file-"; + private static final String RESOURCE = "demoResc"; + private static final int NUMBER_OF_FILES = 3; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private UploadService us; + + @Autowired + private CollectionService cs; + + @Autowired + private FileOperationService fos; + + @Autowired + private PermissionsService permissionsService; + + private String targetPath, parentPath; + + @Before + public void setUp() throws DataGridException { + parentPath = String.format("/%s/home/%s", zone, username); + targetPath = String.format("%s/test-metadata-search-%d", parentPath, System.currentTimeMillis()); + + fos.deleteCollection(targetPath, true); + cs.createCollection(new DataGridCollectionAndDataObject(targetPath, parentPath, true)); + + uploadTestFiles(); + } + + @After + public void tearDown() throws DataGridException { + setOwnOnFiles(); + fos.deleteCollection(targetPath, true); + } + + @Test + public void testSetReadPermissionOnFiles() throws DataGridConnectionRefusedException { + assertPermissionFiles(DataGridPermType.READ, false, false); + } + + @Test + public void testSetWritePermissionOnFiles() throws DataGridConnectionRefusedException { + assertPermissionFiles(DataGridPermType.WRITE, false, false); + } + + @Test + public void testSetOwnPermissionOnFiles() throws DataGridConnectionRefusedException { + assertPermissionFiles(DataGridPermType.OWN, false, false); + } + + @Test + public void testSetNonePermissionOnFiles() throws DataGridConnectionRefusedException { + assertPermissionFiles(DataGridPermType.NONE, false, false); + } + + private void assertPermissionFiles(DataGridPermType permission, boolean recursive, boolean inAdminMode) + throws DataGridConnectionRefusedException { + boolean isPermSet; + + String[] files = new String[NUMBER_OF_FILES]; + for(int i = 0; i < NUMBER_OF_FILES; i++) { + files[i] = String.format("%s/%s", targetPath, BASE_FILE_NAME + i); + } + + isPermSet = permissionsService.setPermissionOnPath(permission, username, recursive, inAdminMode, files); + assertTrue(isPermSet); + } + + private void setOwnOnFiles() throws DataGridConnectionRefusedException { + String[] files = new String[NUMBER_OF_FILES]; + for(int i = 0; i < NUMBER_OF_FILES; i++) { + files[i] = String.format("%s/%s", targetPath, BASE_FILE_NAME + i); + } + + permissionsService.setPermissionOnPath(DataGridPermType.OWN, username, false, false, files); + } + + private void uploadTestFiles() throws DataGridException { + for(int i = 0; i < NUMBER_OF_FILES; i++) { + String filename = BASE_FILE_NAME + i; + MockMultipartFile file = new MockMultipartFile(filename, "Hello World".getBytes()); + us.upload(file, targetPath, false, false, "", RESOURCE, false); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/rules/TestPluginService.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/rules/TestPluginService.java new file mode 100755 index 000000000..efdb56bb4 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/rules/TestPluginService.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.rules; + +import com.emc.metalnx.core.domain.entity.DataGridMSIPkgInfo; +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; +import com.emc.metalnx.services.interfaces.*; +import com.emc.metalnx.services.irods.MSIServiceImpl; +import com.emc.metalnx.services.tests.msi.MSIUtils; +import org.irods.jargon.core.exception.JargonException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyListOf; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.when; + +/** + * Test for Rule Service + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestPluginService { + + public static final String DELIMITER = ","; + @InjectMocks + private MSIService msiService = new MSIServiceImpl(); + + @Mock + private ResourceService mockResourceService; + + @Mock + private RuleService mockRuleService; + + @Mock + private IRODSServices irodsServices; + + @Mock + private ConfigService mockConfigService; + + private static String msiVersion; + private List msiList, mlxMSIList, irods41XMSIs, irods42MSIs, otherMSIList; + + private List servers; + + @PostConstruct + public void init() { + servers = new ArrayList<>(); + MSIUtils msiUtils = new MSIUtils(); + + msiVersion = MSIUtils.getMsiVersion(); + msiList = msiUtils.getMsiList(); + mlxMSIList = msiUtils.getMlxMSIList(); + irods41XMSIs = msiUtils.getIrods41XMSIs(); + irods42MSIs = msiUtils.getIrods420MSIs(); + otherMSIList = msiUtils.getOtherMSIs(); + } + + @Before + public void setUp() throws JargonException, DataGridException { + MockitoAnnotations.initMocks(this); + + DataGridServer s1 = new DataGridServer(); + s1.setHostname("server1.test.com"); + s1.setMSIVersion(msiVersion); + s1.setIp("192.168.0.1"); + s1.setResources(new ArrayList<>()); + + DataGridServer s2 = new DataGridServer(); + s2.setHostname("server2.test.com"); + s2.setMSIVersion(msiVersion); + s2.setIp("192.168.0.2"); + s2.setResources(new ArrayList<>()); + + servers.add(s1); + servers.add(s2); + + when(mockResourceService.getAllResourceServers(anyListOf(DataGridResource.class))).thenReturn(servers); + when(mockRuleService.execGetVersionRule(anyString())).thenReturn(msiVersion); + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(msiList); + + when(mockConfigService.getMlxMSIsExpected()).thenReturn(mlxMSIList); + when(mockConfigService.getIrods41MSIsExpected()).thenReturn(irods41XMSIs); + when(mockConfigService.getIrods42MSIsExpected()).thenReturn(irods42MSIs); + when(mockConfigService.getOtherMSIsExpected()).thenReturn(otherMSIList); + when(mockConfigService.getMsiAPIVersionSupported()).thenReturn(msiVersion); + } + + @Test + public void testMSIListForIRODS420() throws DataGridConnectionRefusedException, DataGridRuleException { + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(new ArrayList<>()); + when(irodsServices.isAtLeastIrods420()).thenReturn(true); + DataGridServer server = msiService.getMSIsInstalled("server1.test.com"); + assertFalse(server.isThereAnyMSI()); + } + + @Test + public void testMSIListForIRODS41X() throws DataGridConnectionRefusedException, DataGridRuleException { + when(mockRuleService.execGetMSIsRule(anyString())).thenReturn(new ArrayList<>()); + when(irodsServices.isAtLeastIrods420()).thenReturn(false); + DataGridServer server = msiService.getMSIsInstalled("server1.test.com"); + assertFalse(server.isThereAnyMSI()); + } + + @Test + public void testMSIInstalledList() throws DataGridConnectionRefusedException { + DataGridServer server = msiService.getMSIsInstalled("server1.test.com"); + Map mlxMSIsMap = server.getMetalnxMSIs(); + Map iRODSMSIsMap = server.getIRODSMSIs(); + Map otherMSIsList = server.getOtherMSIs(); + + for (String msi: irods41XMSIs) assertTrue(iRODSMSIsMap.containsKey(msi)); + for (String msi: mlxMSIList) assertTrue(mlxMSIsMap.containsKey(msi)); + for (String msi: otherMSIList) assertTrue(otherMSIsList.containsKey(msi)); + } + + @Test + public void testNoPkgMissing() throws DataGridConnectionRefusedException, DataGridRuleException { + DataGridMSIPkgInfo msiPkgInfo = msiService.getMSIPkgInfo(); + assertFalse(msiPkgInfo.isThereAnyPkgMissing()); + } + + @Test + public void testNoPkgNotSupported() throws DataGridConnectionRefusedException, DataGridRuleException { + DataGridMSIPkgInfo msiPkgInfo = msiService.getMSIPkgInfo(); + assertFalse(msiPkgInfo.isThereAnyPkgNotSupported()); + } + + @Test + public void testServers() throws DataGridConnectionRefusedException, DataGridRuleException { + DataGridMSIPkgInfo msiPkgInfo = msiService.getMSIPkgInfo(); + assertEquals(2, msiPkgInfo.getServers().size()); + for (DataGridServer server: msiPkgInfo.getServers()) assertEquals(msiVersion, server.getMSIVersion()); + } + + @Test + public void testMSICompatibility() throws DataGridConnectionRefusedException, DataGridRuleException { + DataGridResource resc = new DataGridResource(); + resc.setName("demoResc"); + List rescs = new ArrayList<>(); + rescs.add(resc); + + servers.get(0).setResources(rescs); + + assertTrue(msiService.isMSIAPICompatibleInResc("demoResc")); + } + + @Test + public void testMSICompatibilityInEmptyResc() throws DataGridConnectionRefusedException, DataGridRuleException { + assertFalse(msiService.isMSIAPICompatibleInResc("")); + } +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/rules/TestRuleService.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/rules/TestRuleService.java new file mode 100755 index 000000000..9b566dbc6 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/rules/TestRuleService.java @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.rules; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; + +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.rule.IRODSRuleExecResultOutputParameter; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridRule; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.ConfigService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.ResourceService; +import com.emc.metalnx.services.interfaces.RuleService; +import com.emc.metalnx.services.irods.RuleServiceImpl; + +/** + * Test for Rule Service + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestRuleService { + + private static final String RM_TRASH_RODS_ADMIN_FLAG = "irodsAdminRmTrash="; + private static final String RM_TRASH_RODS_USER_FLAG = "irodsRmTrash="; + private static final String HOST = "icat.test.com"; + private static final String RESOURCE = "demoResc"; + private static String msiVersion; + + @InjectMocks + private RuleService ruleService; + + @Mock + private CollectionService collectionService; + + @Mock + private ResourceService resourceService; + + @Mock + private IRODSServices irodsServices; + + @Mock + private ConfigService configService; + + @PostConstruct + public void init() { + msiVersion = "1.1.0"; + } + + @Before + public void setUp() throws JargonException, DataGridException, URISyntaxException, IOException { + ruleService = spy(RuleServiceImpl.class); // partial mocking + + MockitoAnnotations.initMocks(this); + + when(configService.getIrodsHost()).thenReturn("icat.test.com"); + when(configService.getMsiAPIVersionSupported()).thenReturn(msiVersion); + + DataGridResource resc = new DataGridResource(1, RESOURCE, "zone", "unixfilesystem", "/test/resc/path"); + resc.setHost("icat.test.com"); + when(resourceService.find(anyString())).thenReturn(resc); + when(ruleService.executeRule(anyString())).thenReturn(new HashMap<>()); + } + + @Test + public void testEmptyTrashRuleInAdminMode() throws DataGridConnectionRefusedException, DataGridRuleException { + ruleService.execEmptyTrashRule(RESOURCE, "/tempZone/home/rods", true); + final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(ruleService, times(1)).executeRule(captor.capture()); + final String rule = captor.getValue(); + + assertTrue(rule.contains(RM_TRASH_RODS_ADMIN_FLAG)); + } + + @Test + public void testEmptyTrashRuleAsRodsUser() throws DataGridConnectionRefusedException, DataGridRuleException { + ruleService.execEmptyTrashRule(RESOURCE, "/tempZone/home/rods", false); + final ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(ruleService, times(1)).executeRule(captor.capture()); + final String rule = captor.getValue(); + + assertTrue(rule.contains(RM_TRASH_RODS_USER_FLAG)); + } + + @Test + public void testIRODS42Rule() throws DataGridConnectionRefusedException { + when(irodsServices.isAtLeastIrods420()).thenReturn(true); + DataGridRule rule = new DataGridRule(DataGridRule.VCF_RULE, "icat.test.com", true); + assertTrue(rule.declareRuleOutputParams()); + } + + @Test + public void testRuleWithNoVariableDeclarationForIRODS420() throws DataGridConnectionRefusedException { + DataGridRule rule = new DataGridRule(DataGridRule.ILLUMINA_RULE, "icat.test.com", false); + rule.setInputRuleParams("param1", "param2"); + rule.setOutputRuleParams("output_param"); + assertFalse(rule.toString().contains("*output_param=\"\";")); + } + + @Test + public void testRuleWithVariableDeclarationForIRODS41X() throws DataGridConnectionRefusedException { + DataGridRule rule = new DataGridRule(DataGridRule.ILLUMINA_RULE, "icat.test.com", false); + rule.setInputRuleParams("param1", "param2"); + rule.setOutputRuleParams("output_param"); + assertFalse(rule.toString().contains("*output_param=\"\";")); + } + + @Test + public void testRuleWithInputParams() { + DataGridRule rule = new DataGridRule(DataGridRule.VCF_RULE, "icat.test.com"); + rule.setInputRuleParams("param1", "param2"); + + assertNotNull(rule.toString()); + assertTrue(rule.toString().contains("INPUT *p0=\"param1\", *p1=\"param2\"")); + } + + @Test + public void testRuleWithOutputParams() { + DataGridRule rule = new DataGridRule(DataGridRule.VCF_RULE, "icat.test.com"); + rule.setInputRuleParams("param1", "param2"); + rule.setOutputRuleParams("output_param"); + + assertNotNull(rule.toString()); + assertTrue(rule.toString().contains("INPUT *p0=\"param1\", *p1=\"param2\"")); + assertTrue(rule.toString().contains("OUTPUT *output_param")); + } + + @Test + public void testGetMSIsRule() throws DataGridRuleException, DataGridConnectionRefusedException { + Map result = new HashMap<>(); + IRODSRuleExecResultOutputParameter output = new IRODSRuleExecResultOutputParameter(); + output.setOutputParamType(IRODSRuleExecResultOutputParameter.OutputParamType.STRING); + output.setParameterName("*msis"); + output.setResultObject("libmsi1.so, libmsi2.so, libmsi3.so, libmsi4.so"); + result.put("*msis", output); + + when(ruleService.executeRule(anyString())).thenReturn(result); + + List msis = ruleService.execGetMSIsRule("icat.test.com"); + assertNotNull(msis); + assertFalse(msis.isEmpty()); + assertTrue(msis.contains("libmsi1.so")); + assertTrue(msis.contains("libmsi2.so")); + assertTrue(msis.contains("libmsi3.so")); + assertTrue(msis.contains("libmsi4.so")); + } + + @Test + public void testGetMSIsRuleNoReturn() throws DataGridRuleException, DataGridConnectionRefusedException { + Map result = new HashMap<>(); + IRODSRuleExecResultOutputParameter output = new IRODSRuleExecResultOutputParameter(); + output.setOutputParamType(IRODSRuleExecResultOutputParameter.OutputParamType.STRING); + output.setParameterName("*msis"); + output.setResultObject(null); + result.put("*msis", output); + + when(ruleService.executeRule(anyString())).thenReturn(result); + + List msis = ruleService.execGetMSIsRule(HOST); + assertNotNull(msis); + assertTrue(msis.isEmpty()); + } + + @Test + public void testGetVersionRule() throws DataGridRuleException, DataGridConnectionRefusedException { + Map result = new HashMap<>(); + IRODSRuleExecResultOutputParameter output = new IRODSRuleExecResultOutputParameter(); + output.setOutputParamType(IRODSRuleExecResultOutputParameter.OutputParamType.STRING); + output.setParameterName("*version"); + output.setResultObject(msiVersion); + result.put("*version", output); + + when(ruleService.executeRule(anyString())).thenReturn(result); + + String version = ruleService.execGetVersionRule("icat.test.com"); + assertEquals(msiVersion, version); + } + + @Test + public void testReplicateObjRule() throws DataGridRuleException, DataGridConnectionRefusedException { + String path = "this/is/a/path"; + + ruleService.execReplDataObjRule(RESOURCE, path, false); + + verify(resourceService, times(1)).find(RESOURCE); + verify(ruleService, times(1)).executeRule(anyString()); + } + + @Test + public void testReplicateObjRuleInAdminMode() throws DataGridRuleException, DataGridConnectionRefusedException { + String path = "this/is/a/path"; + + ruleService.execReplDataObjRule(RESOURCE, path, true); + + verify(resourceService, times(1)).find(RESOURCE); + verify(ruleService, times(1)).executeRule(anyString()); + } + + @Test + public void testPopulateMetadataRule() throws DataGridRuleException, DataGridConnectionRefusedException { + when(configService.isPopulateMsiEnabled()).thenReturn(true); + ruleService.execPopulateMetadataRule(HOST, "/testZone/home/rods"); + verify(ruleService, times(1)).executeRule(anyString()); + } + + @Test + public void testImageRule() throws DataGridRuleException, DataGridConnectionRefusedException { + ruleService.execImageRule(HOST, "/zone/home/rods/test.jpg", "/var/lib/irods/test.jpg"); + verify(ruleService, times(1)).executeRule(anyString()); + } + + @Test + public void testVCFMetadataRule() throws DataGridRuleException, DataGridConnectionRefusedException { + ruleService.execVCFMetadataRule(HOST, "/zone/home/rods/test.vcf", "/var/lib/irods/test.vcf"); + verify(ruleService, times(1)).executeRule(anyString()); + } + + @Test + public void testBamCramMetadataRule() throws DataGridRuleException, DataGridConnectionRefusedException { + ruleService.execBamCramMetadataRule(HOST, "/zone/home/rods/test.bam", "/var/lib/irods/test.bam"); + verify(ruleService, times(1)).executeRule(anyString()); + } + + @Test + public void testManifestFileRule() throws Exception { + when(collectionService.getSubCollectionsAndDataObjectsUnderPath(anyString())).thenReturn(new ArrayList<>()); + ruleService.execManifestFileRule(HOST, "/zone/home/rods", "/zone/home/rods/test.xml", + "/var/lib/irods/test.xml"); + verify(ruleService, atMost(1)).executeRule(anyString()); + } + + @Test + public void testIlluminaMetadataRule() throws DataGridRuleException, DataGridConnectionRefusedException { + DataGridResource dgDestResc = resourceService.find(RESOURCE); + ruleService.execIlluminaMetadataRule(dgDestResc, "/zone/home/rods", "/zone/home/rods/test_SSU.tar"); + + // these two methods should never be called since there is no objects under the + // test path + verify(ruleService, atMost(2)).executeRule(anyString()); + } + + @Test + public void testDeploymentRule() throws DataGridConnectionRefusedException, DataGridRuleException { + when(irodsServices.isAtLeastIrods420()).thenReturn(true); + + String ruleName = "test_deployment_rule.re"; + String ruleVaultPath = String.format("/var/lib/irods/Vault/.rulecache/%s", ruleName); + ruleService.execDeploymentRule(configService.getIrodsHost(), ruleName, ruleVaultPath); + verify(ruleService, times(1)).executeRule(anyString()); + } + + @Test + public void testDeploymentRuleNotIRods42() throws DataGridConnectionRefusedException, DataGridRuleException { + when(irodsServices.isAtLeastIrods420()).thenReturn(false); + + String ruleName = "test_deployment_rule.re"; + String ruleVaultPath = String.format("/var/lib/irods/Vault/.rulecache/%s", ruleName); + ruleService.execDeploymentRule(configService.getIrodsHost(), ruleName, ruleVaultPath); + verify(ruleService, never()).executeRule(anyString()); + } + + @Test(expected = DataGridRuleException.class) + public void testDeploymentRuleException() throws DataGridConnectionRefusedException, DataGridRuleException { + when(irodsServices.isAtLeastIrods420()).thenReturn(true); + doThrow(DataGridRuleException.class).when(ruleService).executeRule(anyString()); + + String ruleName = "test_deployment_rule.re"; + String ruleVaultPath = String.format("/var/lib/irods/Vault/.rulecache/%s", ruleName); + ruleService.execDeploymentRule(configService.getIrodsHost(), ruleName, ruleVaultPath); + verify(ruleService, times(1)).executeRule(anyString()); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/serverutil/TestServerUtil.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/serverutil/TestServerUtil.java new file mode 100755 index 000000000..157d68166 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/serverutil/TestServerUtil.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.serverutil; + +import com.emc.metalnx.core.domain.entity.DataGridServer; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.machine.enums.ServerRequestInfoType; +import com.emc.metalnx.services.machine.util.ServerUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static org.junit.Assert.assertEquals; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestServerUtil { + + @Test + public void testNullJsonResponse() throws DataGridConnectionRefusedException { + DataGridServer server = new DataGridServer(); + server.setHostname("icat.test.com"); + ServerUtil.populateDataGridServerStatus(null, server); + + assertEquals(server.getMachineStatus(), ServerRequestInfoType.WARNING_STATUS.toString()); + assertEquals(server.getDataGridStatus(), ServerRequestInfoType.WARNING_STATUS.toString()); + assertEquals(server.getDiskStatus(), ServerRequestInfoType.WARNING_STATUS.toString()); + assertEquals(server.getMemoryStatus(), ServerRequestInfoType.WARNING_STATUS.toString()); + } + + @Test + public void testEmptyJsonResponse() throws DataGridConnectionRefusedException { + DataGridServer server = new DataGridServer(); + server.setHostname("icat.test.com"); + ServerUtil.populateDataGridServerStatus("", server); + + assertEquals(server.getMachineStatus(), ServerRequestInfoType.WARNING_STATUS.toString()); + assertEquals(server.getDataGridStatus(), ServerRequestInfoType.WARNING_STATUS.toString()); + assertEquals(server.getDiskStatus(), ServerRequestInfoType.WARNING_STATUS.toString()); + assertEquals(server.getMemoryStatus(), ServerRequestInfoType.WARNING_STATUS.toString()); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithByteLimit.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithByteLimit.java new file mode 100755 index 000000000..787aea0db --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithByteLimit.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.ticketclient; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import com.emc.metalnx.services.tests.tickets.TestTicketUtils; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.io.File; +import java.io.IOException; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestTicketWithByteLimit { + private static final int WRITE_BYTE_LIMIT = 1; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketClientService ticketClientService; + + @Autowired + private IRODSServices irodsServices; + + private String ticketString, targetPath, filePath; + private TestTicketUtils ticketUtils; + private File localFile; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + ticketUtils.setWriteByteLimit(ticketString, WRITE_BYTE_LIMIT); + localFile = ticketUtils.createLocalFile(); + filePath = String.format("%s/%s", targetPath, localFile.getName()); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile); + ticketUtils.deleteTicket(ticketString); + ticketUtils.deleteIRODSFile(filePath); + } + + @Test(expected = DataGridTicketUploadException.class) + public void testTicketWithWriteByteLimit() throws DataGridTicketUploadException, DataGridTicketInvalidUserException { + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile, targetPath); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithExpirationDate.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithExpirationDate.java new file mode 100755 index 000000000..b5dfe50c2 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithExpirationDate.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.ticketclient; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import com.emc.metalnx.services.tests.tickets.TestTicketUtils; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.Calendar; +import java.util.Date; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestTicketWithExpirationDate { + private static final Date EXPIRATION_DATE = new Date(); + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketClientService ticketClientService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString, filePath; + private TestTicketUtils ticketUtils; + private File localFile; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + ticketUtils.setExpirationDate(ticketString, yesterday()); + localFile = ticketUtils.createLocalFile(); + filePath = String.format("%s/%s", targetPath, localFile.getName()); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile); + ticketUtils.deleteTicket(ticketString); + ticketUtils.deleteIRODSFile(filePath); + } + + @Test(expected = DataGridTicketUploadException.class) + public void testTicketWithExpirationDate() throws DataGridTicketUploadException, DataGridTicketInvalidUserException { + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile, targetPath); + } + + private Date yesterday() { + final Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, -1); + return cal.getTime(); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithFilesLimit.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithFilesLimit.java new file mode 100755 index 000000000..aa48b319c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithFilesLimit.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.ticketclient; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import com.emc.metalnx.services.tests.tickets.TestTicketUtils; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.io.File; +import java.io.IOException; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestTicketWithFilesLimit { + private static final int WRITE_FILE_LIMIT = 1; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketClientService ticketClientService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString, filePath1, filePath2; + private TestTicketUtils ticketUtils; + private File localFile1, localFile2; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + ticketUtils.setWriteFileLimit(ticketString, WRITE_FILE_LIMIT); + localFile1 = ticketUtils.createLocalFile(); + localFile2 = ticketUtils.createLocalFile("test-ticket-2-" + System.currentTimeMillis()); + filePath1 = String.format("%s/%s", targetPath, localFile1.getName()); + filePath2 = String.format("%s/%s", targetPath, localFile2.getName()); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile1); + FileUtils.deleteQuietly(localFile2); + ticketUtils.deleteTicket(ticketString); + ticketUtils.deleteIRODSFile(filePath1); + ticketUtils.deleteIRODSFile(filePath2); + } + + @Test(expected = DataGridTicketUploadException.class) + public void testTicketWithFileLimit() throws DataGridTicketUploadException, DataGridTicketInvalidUserException { + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile1, targetPath); + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile2, targetPath); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithHostRestriction.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithHostRestriction.java new file mode 100755 index 000000000..ad5798c3f --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithHostRestriction.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.ticketclient; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import com.emc.metalnx.services.tests.tickets.TestTicketUtils; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.io.File; +import java.io.IOException; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestTicketWithHostRestriction { + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Value("${irods.host}") + private String host; + + @Autowired + private TicketClientService ticketClientService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString, filePath; + private TestTicketUtils ticketUtils; + private File localFile; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + ticketUtils.addHostRestriction(ticketString, host); + localFile = ticketUtils.createLocalFile(); + filePath = String.format("%s/%s", targetPath, localFile.getName()); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile); + ticketUtils.deleteTicket(ticketString); + ticketUtils.deleteIRODSFile(filePath); + } + + @Test(expected = DataGridTicketUploadException.class) + public void testTicketWithHostRestriction() throws DataGridTicketUploadException, DataGridTicketInvalidUserException { + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile, targetPath); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithUserRestriction.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithUserRestriction.java new file mode 100755 index 000000000..f726c202c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithUserRestriction.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.ticketclient; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import com.emc.metalnx.services.tests.tickets.TestTicketUtils; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.io.File; +import java.io.IOException; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestTicketWithUserRestriction { + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketClientService ticketClientService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString, filePath; + private TestTicketUtils ticketUtils; + private File localFile; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + ticketUtils.addUserRestriction(ticketString, username); + localFile = ticketUtils.createLocalFile(); + filePath = String.format("%s/%s", targetPath, localFile.getName()); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile); + ticketUtils.deleteTicket(ticketString); + ticketUtils.deleteIRODSFile(filePath); + } + + @Test(expected = DataGridTicketUploadException.class) + public void testTicketWithUserRestriction() throws DataGridTicketUploadException, DataGridTicketInvalidUserException { + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile, targetPath); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithUsesLimit.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithUsesLimit.java new file mode 100755 index 000000000..16fbf0e71 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/ticketclient/TestTicketWithUsesLimit.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.ticketclient; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketClientService; +import com.emc.metalnx.services.tests.tickets.TestTicketUtils; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.io.File; +import java.io.IOException; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestTicketWithUsesLimit { + private static final int USES_LIMIT = 1; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketClientService ticketClientService; + + @Autowired + private IRODSServices irodsServices; + + private String ticketString, targetPath, filePath1, filePath2; + private TestTicketUtils ticketUtils; + private File localFile, localFile2; + + @Before + public void setUp() throws DataGridException, JargonException, IOException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + ticketUtils.setUsesLimit(ticketString, USES_LIMIT); + localFile = ticketUtils.createLocalFile(); + localFile2 = ticketUtils.createLocalFile("ticket-file-2-" + System.currentTimeMillis()); + filePath1 = String.format("%s/%s", targetPath, localFile.getName()); + filePath2 = String.format("%s/%s", targetPath, localFile2.getName()); + } + + @After + public void tearDown() throws JargonException, DataGridConnectionRefusedException { + FileUtils.deleteQuietly(localFile); + FileUtils.deleteQuietly(localFile2); + ticketUtils.deleteTicket(ticketString); + ticketUtils.deleteIRODSFile(filePath1); + ticketUtils.deleteIRODSFile(filePath2); + } + + @Ignore + @Test(expected = DataGridTicketUploadException.class) + public void testTicketWithUsesLimit() throws DataGridTicketUploadException, DataGridTicketInvalidUserException { + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile, targetPath); + ticketClientService.transferFileToIRODSUsingTicket(ticketString, localFile2, targetPath); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateDuplicatedTicket.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateDuplicatedTicket.java new file mode 100755 index 000000000..c6c7ce96d --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateDuplicatedTicket.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCreateDuplicatedTicket { + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE); + } + + @After + public void tearDown() throws JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test(expected = DataGridTicketException.class) + public void testCreateDuplicatedTicket() throws DataGridConnectionRefusedException, DataGridTicketException { + DataGridTicket dgt = new DataGridTicket(targetPath); + dgt.setTicketString(ticketString); + ticketService.create(dgt); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicket.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicket.java new file mode 100755 index 000000000..ecc9d3fa7 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicket.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static junit.framework.Assert.assertFalse; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCreateTicket { + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + } + + @After + public void tearDown() throws JargonException { + if (ticketString != null && !ticketString.isEmpty() ) { + ticketUtils.deleteTicket(ticketString); + } + } + + @Test + public void testCreateTicket() throws DataGridConnectionRefusedException, DataGridTicketException { + ticketString = ticketService.create(new DataGridTicket(targetPath)); + assertFalse(ticketString.isEmpty()); + } + + @Test(expected = DataGridTicketException.class) + public void testCreateNullTicket() throws DataGridConnectionRefusedException, DataGridTicketException { + ticketString = ticketService.create(null); + } + + @Test(expected = DataGridTicketException.class) + public void testCreateTicketWithMissingPath() throws DataGridConnectionRefusedException, DataGridTicketException { + ticketString = ticketService.create(new DataGridTicket()); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithByteLimit.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithByteLimit.java new file mode 100755 index 000000000..482aed6cb --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithByteLimit.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.Ticket; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCreateTicketWithByteLimit { + private static final int WRITE_BYTE_LIMIT = 1024; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + private DataGridTicket dgt; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + + dgt = new DataGridTicket(targetPath); + dgt.setWriteByteLimit(WRITE_BYTE_LIMIT); + } + + @After + public void tearDown() throws JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testCreateTicketWithWriteByteLimit() throws DataGridConnectionRefusedException, DataGridTicketException, + JargonException { + ticketString = ticketService.create(dgt); + Ticket ticketWithByteLimit = ticketUtils.findTicket(ticketString); + + assertEquals(WRITE_BYTE_LIMIT, ticketWithByteLimit.getWriteByteLimit()); + assertFalse(ticketWithByteLimit.getTicketString().isEmpty()); + assertTrue(ticketWithByteLimit.getIrodsAbsolutePath().equals(targetPath)); + assertTrue(ticketWithByteLimit.getOwnerName().equals(username)); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithExpirationDate.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithExpirationDate.java new file mode 100755 index 000000000..13d0a4b41 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithExpirationDate.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.Ticket; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCreateTicketWithExpirationDate { + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private Date date; + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + private DataGridTicket dgt; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + + date = new Date(); + dgt = new DataGridTicket(targetPath); + dgt.setExpirationDate(date); + } + + @After + public void tearDown() throws JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testCreateTicketWithExpirationDate() throws DataGridConnectionRefusedException, DataGridTicketException, + JargonException { + ticketString = ticketService.create(dgt); + Ticket ticketWithExpirationDate = ticketUtils.findTicket(ticketString); + + String currDate = dateFormat.format(date); + String ticketCreatedDate = dateFormat.format(ticketWithExpirationDate.getExpireTime()); + + assertEquals(currDate, ticketCreatedDate); + assertFalse(ticketWithExpirationDate.getTicketString().isEmpty()); + assertTrue(ticketWithExpirationDate.getIrodsAbsolutePath().equals(targetPath)); + assertTrue(ticketWithExpirationDate.getOwnerName().equals(username)); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithFilesLimit.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithFilesLimit.java new file mode 100755 index 000000000..af57ab2fc --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithFilesLimit.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.Ticket; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCreateTicketWithFilesLimit { + private static final int WRITE_FILE_LIMIT = 5; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + private DataGridTicket dgt; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + + dgt = new DataGridTicket(targetPath); + dgt.setWriteFileLimit(WRITE_FILE_LIMIT); + } + + @After + public void tearDown() throws JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testCreateTicketWithWriteFileLimit() throws DataGridConnectionRefusedException, DataGridTicketException, + JargonException { + ticketString = ticketService.create(dgt); + Ticket ticketWithFileLimit = ticketUtils.findTicket(ticketString); + + assertEquals(WRITE_FILE_LIMIT, ticketWithFileLimit.getWriteFileLimit()); + assertFalse(ticketWithFileLimit.getTicketString().isEmpty()); + assertTrue(ticketWithFileLimit.getIrodsAbsolutePath().equals(targetPath)); + assertTrue(ticketWithFileLimit.getOwnerName().equals(username)); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithHostRestriction.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithHostRestriction.java new file mode 100755 index 000000000..83a0c43f1 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithHostRestriction.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCreateTicketWithHostRestriction { + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Value("${irods.host}") + private String host; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + private DataGridTicket dgt; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + dgt = new DataGridTicket(targetPath); + dgt.addHost(host); + } + + @After + public void tearDown() throws JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testCreateTicketWithHostRestriction() throws DataGridConnectionRefusedException, DataGridTicketException, JargonException { + ticketString = ticketService.create(dgt); + List hosts = ticketUtils.listAllHostRestrictionsForSpecifiedTicket(ticketString); + assertEquals(1, hosts.size()); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithUserRestriction.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithUserRestriction.java new file mode 100755 index 000000000..644d183e5 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithUserRestriction.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCreateTicketWithUserRestriction { + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + private DataGridTicket dgt; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + + dgt = new DataGridTicket(targetPath); + dgt.addUser(username); + } + + @After + public void tearDown() throws JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testCreateTicketWithHostRestriction() throws DataGridConnectionRefusedException, + DataGridTicketException, JargonException { + ticketString = ticketService.create(dgt); + List users = ticketUtils.listAllUserRestrictionsForSpecifiedTicket(ticketString); + + assertEquals(1, users.size()); + assertTrue(users.contains(username)); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithUsesLimit.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithUsesLimit.java new file mode 100755 index 000000000..9d63f6834 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestCreateTicketWithUsesLimit.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.Ticket; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestCreateTicketWithUsesLimit { + private static final int USES_LIMIT = 5; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + private DataGridTicket dgt; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + + dgt = new DataGridTicket(targetPath); + dgt.setUsesLimit(USES_LIMIT); + } + + @After + public void tearDown() throws JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testCreateTicketWithExpirationDate() throws DataGridConnectionRefusedException, DataGridTicketException, + JargonException { + ticketString = ticketService.create(dgt); + Ticket ticketWithUses = ticketUtils.findTicket(ticketString); + + assertEquals(USES_LIMIT, ticketWithUses.getUsesLimit()); + assertFalse(ticketWithUses.getTicketString().isEmpty()); + assertTrue(ticketWithUses.getIrodsAbsolutePath().equals(targetPath)); + assertTrue(ticketWithUses.getOwnerName().equals(username)); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestDeleteTickets.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestDeleteTickets.java new file mode 100755 index 000000000..0be409168 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestDeleteTickets.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestDeleteTickets { + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private TestTicketUtils ticketUtils; + private List ticketStrings; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + ticketUtils = new TestTicketUtils(irodsServices); + ticketStrings = new ArrayList<>(); + ticketStrings.add(ticketUtils.createTicket(parentPath, username)); + ticketStrings.add(ticketUtils.createTicket(parentPath, username, TicketCreateModeEnum.WRITE)); + irodsServices.getIrodsAccessObjectFactory().closeSessionAndEatExceptions(); + } + + @After + public void tearDown() throws DataGridConnectionRefusedException, JargonException { + ticketUtils.deleteAllTicketsForThisUser(); + } + + @Ignore + public void testBulkDelete() throws DataGridConnectionRefusedException, JargonException { + ticketService.bulkDelete(ticketStrings); + assertTrue(ticketUtils.listAllTicketsForUser().isEmpty()); + } + + @Test + public void testDeleteSingleTicket() throws DataGridConnectionRefusedException, JargonException { + ticketService.delete(ticketStrings.get(0)); + assertFalse(ticketUtils.listAllTicketsForUser().isEmpty()); + } + + @Test + public void testDeleteWithNullString() throws DataGridConnectionRefusedException { + assertFalse(ticketService.delete(null)); + } + + @Test + public void testBulkDeleteWithNullString() throws DataGridConnectionRefusedException { + assertFalse(ticketService.bulkDelete(null)); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestFindTicket.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestFindTicket.java new file mode 100755 index 000000000..d570e3e14 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestFindTicket.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketNotFoundException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.Calendar; +import java.util.Date; + +import static junit.framework.Assert.*; +import static org.junit.Assert.assertTrue; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestFindTicket { + private static final int USES_LIMIT = 5; + private static final Date EXPIRATION_DATE = new Date(); + private static final long WRITE_BYTE_LIMIT = 1024; + private static final int USES_COUNT = 0; + private static final int WRITE_BYTE_COUNT = 0; + private static final int WRITE_FILE_LIMIT = 5; + private static final int WRITE_FILE_COUNT = 0; + private static final String[] HOSTS = {"test-ticket-host1", "test-ticket-host2"}; + private static final String PUBLIC_GROUP = "public"; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Value("${irods.host}") + private String host; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + + ticketString = ticketUtils.createTicket(parentPath, username); + ticketUtils.setUsesLimit(ticketString, USES_LIMIT); + ticketUtils.setExpirationDate(ticketString, EXPIRATION_DATE); + ticketUtils.setWriteByteLimit(ticketString, WRITE_BYTE_LIMIT); + ticketUtils.setWriteFileLimit(ticketString, WRITE_FILE_LIMIT); + ticketUtils.addHostRestriction(ticketString, host); + ticketUtils.addUserRestriction(ticketString, username); + ticketUtils.addGroupRestriction(ticketString, PUBLIC_GROUP); + } + + @After + public void tearDown() throws DataGridConnectionRefusedException, JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testFindTicket() throws DataGridConnectionRefusedException, DataGridTicketNotFoundException { + DataGridTicket dgt = ticketService.find(ticketString); + assertNotNull(dgt); + assertFalse(dgt.getTicketString().isEmpty()); + assertTrue(dgt.getPath().equals(targetPath)); + assertTrue(dgt.getOwner().equals(username)); + assertDate(EXPIRATION_DATE, dgt.getExpirationDate()); + assertEquals(USES_LIMIT, dgt.getUsesLimit()); + assertEquals(USES_COUNT, dgt.getUsesCount()); + assertEquals(WRITE_BYTE_LIMIT, dgt.getWriteByteLimit()); + assertEquals(WRITE_BYTE_COUNT, dgt.getWriteByteCount()); + assertEquals(WRITE_FILE_LIMIT, dgt.getWriteFileLimit()); + assertEquals(WRITE_FILE_COUNT, dgt.getWriteFileCount()); + assertEquals(1, dgt.getHosts().size()); + assertEquals(1, dgt.getUsers().size()); + assertTrue(dgt.getUsers().contains(username)); + assertTrue(dgt.getGroups().contains(PUBLIC_GROUP)); + } + + @Test(expected = DataGridTicketNotFoundException.class) + public void testFindNonExistentTicket() throws DataGridConnectionRefusedException, DataGridTicketNotFoundException { + String random = "" + System.currentTimeMillis(); + ticketService.find(random); + } + + public void assertDate(Date date1, Date date2) { + Calendar cal1 = Calendar.getInstance(); + Calendar cal2 = Calendar.getInstance(); + cal1.setTime(date1); + cal2.setTime(date2); + + boolean sameYear = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR); + boolean sameMonth = cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH); + boolean sameDay = cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); + boolean sameTime = cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE); + + assertTrue(sameYear); + assertTrue(sameMonth); + assertTrue(sameDay); + assertTrue(sameTime); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestModifyTicket.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestModifyTicket.java new file mode 100755 index 000000000..c6c4e9543 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestModifyTicket.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestModifyTicket { + private static final int USES_LIMIT = 5; + private static final long WRITE_BYTE_LIMIT = 1024; + private static final int WRITE_FILE_LIMIT = 5; + private static final String PUBLIC_GROUP = "public"; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Value("${irods.host}") + private String host; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String targetPath, ticketString; + private TestTicketUtils ticketUtils; + private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + @Before + public void setUp() throws DataGridException, JargonException { + String parentPath = String.format("/%s/home", zone); + targetPath = String.format("%s/%s", parentPath, username); + ticketUtils = new TestTicketUtils(irodsServices); + + ticketString = ticketUtils.createTicket(parentPath, username); + } + + @After + public void tearDown() throws DataGridConnectionRefusedException, JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testModifyTicketExpireDate() throws DataGridConnectionRefusedException, DataGridTicketException { + Date date = new Date(); + DataGridTicket dgt = new DataGridTicket(targetPath); + dgt.setTicketString(ticketString); + dgt.setExpirationDate(date); + + DataGridTicket dgtModified = ticketService.modify(dgt); + + String currDate = dateFormat.format(date); + String ticketModifiedDate = dateFormat.format(dgtModified.getExpirationDate()); + + assertEquals(currDate, ticketModifiedDate); + assertFalse(dgtModified.getTicketString().isEmpty()); + assertTrue(dgtModified.getPath().equals(targetPath)); + assertTrue(dgtModified.getOwner().equals(username)); + } + + @Test + public void testModifyTicketUsesLimit() throws DataGridConnectionRefusedException, DataGridTicketException { + + int newUsesLimit = USES_LIMIT + 1; + DataGridTicket dgt = new DataGridTicket(targetPath); + dgt.setTicketString(ticketString); + dgt.setUsesLimit(newUsesLimit); + + DataGridTicket ticketModified = ticketService.modify(dgt); + + assertEquals(newUsesLimit, ticketModified.getUsesLimit()); + assertFalse(ticketModified.getTicketString().isEmpty()); + assertTrue(ticketModified.getPath().equals(targetPath)); + assertTrue(ticketModified.getOwner().equals(username)); + } + + @Test + public void testModifyTicketWriteByteLimit() throws DataGridConnectionRefusedException, DataGridTicketException { + + long newWriteByteLimit = 2 * WRITE_BYTE_LIMIT; + DataGridTicket dgt = new DataGridTicket(targetPath); + dgt.setTicketString(ticketString); + dgt.setWriteByteLimit(newWriteByteLimit); + + DataGridTicket ticketModified = ticketService.modify(dgt); + + assertEquals(newWriteByteLimit, ticketModified.getWriteByteLimit()); + assertFalse(ticketModified.getTicketString().isEmpty()); + assertTrue(ticketModified.getPath().equals(targetPath)); + assertTrue(ticketModified.getOwner().equals(username)); + } + + @Test + public void testModifyTicketWriteFileLimit() throws DataGridConnectionRefusedException, DataGridTicketException { + + int newWriteFileLimit = WRITE_FILE_LIMIT + 1; + DataGridTicket dgt = new DataGridTicket(targetPath); + dgt.setTicketString(ticketString); + dgt.setWriteFileLimit(newWriteFileLimit); + + DataGridTicket ticketModified = ticketService.modify(dgt); + + assertEquals(newWriteFileLimit, ticketModified.getWriteFileLimit()); + assertFalse(ticketModified.getTicketString().isEmpty()); + assertTrue(ticketModified.getPath().equals(targetPath)); + assertTrue(ticketModified.getOwner().equals(username)); + } + + @Test + public void testModifyTicketHosts() throws DataGridConnectionRefusedException, DataGridTicketException { + + DataGridTicket dgt = new DataGridTicket(targetPath); + dgt.setTicketString(ticketString); + dgt.addHost(host); + + DataGridTicket ticketModified = ticketService.modify(dgt); + + assertEquals(1, ticketModified.getHosts().size()); + assertFalse(ticketModified.getTicketString().isEmpty()); + assertTrue(ticketModified.getPath().equals(targetPath)); + assertTrue(ticketModified.getOwner().equals(username)); + } + + @Test + public void testModifyTicketUsers() throws DataGridConnectionRefusedException, DataGridTicketException { + + DataGridTicket dgt = new DataGridTicket(targetPath); + dgt.setTicketString(ticketString); + dgt.addUser(username); + + DataGridTicket ticketModified = ticketService.modify(dgt); + + assertEquals(1, ticketModified.getUsers().size()); + assertTrue(ticketModified.getUsers().contains(username)); + assertFalse(ticketModified.getTicketString().isEmpty()); + assertTrue(ticketModified.getPath().equals(targetPath)); + assertTrue(ticketModified.getOwner().equals(username)); + } + + @Test + public void testModifyTicketGroups() throws DataGridConnectionRefusedException, DataGridTicketException { + + DataGridTicket dgt = new DataGridTicket(targetPath); + dgt.setTicketString(ticketString); + dgt.addGroup(PUBLIC_GROUP); + + DataGridTicket ticketModified = ticketService.modify(dgt); + + assertEquals(1, ticketModified.getGroups().size()); + assertTrue(ticketModified.getGroups().contains(PUBLIC_GROUP)); + assertFalse(ticketModified.getTicketString().isEmpty()); + assertTrue(ticketModified.getPath().equals(targetPath)); + assertTrue(ticketModified.getOwner().equals(username)); + } + +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestTicketService.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestTicketService.java new file mode 100755 index 000000000..1964666cc --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestTicketService.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.TicketService; +import org.irods.jargon.core.exception.JargonException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import java.util.List; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; + +/** + * Test iRODS services. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestTicketService { + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String username; + + @Autowired + private TicketService ticketService; + + @Autowired + private IRODSServices irodsServices; + + private String ticketString, parentPath; + private long time; + private TestTicketUtils ticketUtils; + + @Before + public void setUp() throws DataGridException, JargonException { + time = System.currentTimeMillis(); + parentPath = String.format("/%s/home", zone); + ticketUtils = new TestTicketUtils(irodsServices); + ticketString = ticketUtils.createTicket(parentPath, username); + } + + @After + public void tearDown() throws DataGridException, JargonException { + ticketUtils.deleteTicket(ticketString); + } + + @Test + public void testListingAllTickets() throws DataGridConnectionRefusedException { + List tickets = ticketService.findAll(); + assertNotNull(tickets); + assertFalse(tickets.isEmpty()); + + for(DataGridTicket t: tickets) { + assertNotNull(t.getPath()); + assertFalse(t.getTicketString().isEmpty()); + assertFalse(t.getOwner().isEmpty()); + assertFalse(t.getTicketString().isEmpty()); + } + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestTicketUtils.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestTicketUtils.java new file mode 100755 index 000000000..78b217060 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/tickets/TestTicketUtils.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.tickets; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.IRODSServices; +import org.apache.commons.io.FileUtils; +import org.irods.jargon.core.exception.JargonException; +import org.irods.jargon.core.pub.io.IRODSFile; +import org.irods.jargon.ticket.Ticket; +import org.irods.jargon.ticket.TicketAdminService; +import org.irods.jargon.ticket.packinstr.TicketCreateModeEnum; + +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.List; + +/** + * Utils class for ticket operations during tests. + */ +public class TestTicketUtils { + private IRODSServices irodsServices; + private TicketAdminService ticketAdminService; + public static final String TICKET_FILE_CONTENT = "This is a test for ticket"; + + public TestTicketUtils(IRODSServices irodsServices) throws DataGridConnectionRefusedException { + this.irodsServices = irodsServices; + this.ticketAdminService = irodsServices.getTicketAdminService(); + } + + public List listAllTicketsForUser() throws JargonException { + return ticketAdminService.listAllTickets(0); + } + + public void deleteIRODSFile(String path) throws JargonException, DataGridConnectionRefusedException { + IRODSFile ticketIRODSFile = irodsServices.getIRODSFileFactory().instanceIRODSFile(path); + if(ticketIRODSFile != null && ticketIRODSFile.exists()) { + irodsServices.getIRODSFileSystemAO().fileDeleteForce(ticketIRODSFile); + } + } + + public String createTicket(String parentPath, String item, TicketCreateModeEnum type) throws JargonException, DataGridConnectionRefusedException { + IRODSFile irodsFile = irodsServices.getIRODSFileFactory().instanceIRODSFile(parentPath, item); + return ticketAdminService.createTicket(type, irodsFile, ""); + } + + public String createTicket(String parentPath, String item) throws JargonException, DataGridConnectionRefusedException { + return createTicket(parentPath, item, TicketCreateModeEnum.READ); + } + + public void deleteTicket(String ticketString) throws JargonException { + ticketAdminService.deleteTicket(ticketString); + } + + public void deleteAllTicketsForThisUser() throws JargonException { + ticketAdminService.deleteAllTicketsForThisUser(); + } + + public void setUsesLimit(String ticketString, int usesLimit) throws JargonException { + ticketAdminService.setTicketUsesLimit(ticketString, usesLimit); + } + + public void setExpirationDate(String ticketString, Date expirationDate) + throws DataGridConnectionRefusedException, JargonException { + ticketAdminService.setTicketExpiration(ticketString, expirationDate); + } + + public void setWriteByteLimit(String ticketString, long writeByteLimit) throws JargonException { + ticketAdminService.setTicketByteWriteLimit(ticketString, writeByteLimit); + } + + public void setWriteFileLimit(String ticketString, int writeFileLimit) throws JargonException { + ticketAdminService.setTicketFileWriteLimit(ticketString, writeFileLimit); + } + + public void addHostRestriction(String ticketString, String host) throws JargonException { + ticketAdminService.addTicketHostRestriction(ticketString, host); + } + + public void addUserRestriction(String ticketString, String username) throws JargonException { + ticketAdminService.addTicketUserRestriction(ticketString, username); + } + + public void addGroupRestriction(String ticketString, String group) throws JargonException { + ticketAdminService.addTicketGroupRestriction(ticketString, group); + } + + public Ticket findTicket(String ticketString) throws JargonException { + return ticketAdminService.getTicketForSpecifiedTicketString(ticketString); + } + + public List listAllHostRestrictionsForSpecifiedTicket(String ticketString) throws JargonException { + return ticketAdminService.listAllHostRestrictionsForSpecifiedTicket(ticketString, 0); + } + + public List listAllUserRestrictionsForSpecifiedTicket(String ticketString) throws JargonException { + return ticketAdminService.listAllUserRestrictionsForSpecifiedTicket(ticketString, 0); + } + + public List listAllGroupRestrictionsForSpecifiedTicket(String ticketString) throws JargonException { + return ticketAdminService.listAllGroupRestrictionsForSpecifiedTicket(ticketString, 0); + } + + public File createLocalFile(String filename) throws IOException { + File file = new File(filename); + FileUtils.writeByteArrayToFile(file, TICKET_FILE_CONTENT.getBytes()); + return file; + } + + public File createLocalFile() throws IOException { + return createLocalFile("test-ticket-file-" + System.currentTimeMillis()); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/upload/TestUploadService.java b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/upload/TestUploadService.java new file mode 100755 index 000000000..6cda3dab0 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/java/com/emc/metalnx/services/tests/upload/TestUploadService.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.services.tests.upload; + +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridFileAlreadyExistsException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.UploadService; + +/** + * Test Upload. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration("classpath:test-services-context.xml") +@WebAppConfiguration +public class TestUploadService { + + public static final String TEST_FILE_NAME = "test.txt"; + public static final String RESOURCE = "demoResc"; + + @Value("${irods.zoneName}") + private String zone; + + @Value("${jobs.irods.username}") + private String user; + + @Autowired + private UploadService us; + + @Autowired + private FileOperationService fs; + + @Autowired + private CollectionService cs; + + private String targetPath; + + @Before + public void setUp() throws DataGridConnectionRefusedException { + targetPath = String.format("/%s/home/%s", zone, user); + fs.deleteItem(targetPath + "/" + TEST_FILE_NAME, true); + } + + @After + public void tearDown() throws DataGridConnectionRefusedException { + fs.deleteItem(targetPath + "/" + TEST_FILE_NAME, true); + } + + @Test + public void testDirectTransfer() throws Exception { + boolean fileUploaded = false; + + MockMultipartFile file = new MockMultipartFile(TEST_FILE_NAME, "Hello World".getBytes()); + + us.upload(file, targetPath, false, false, "", RESOURCE, false); + + List items = cs.getSubCollectionsAndDataObjectsUnderPath(targetPath); + + for (DataGridCollectionAndDataObject item : items) { + if (TEST_FILE_NAME.equals(item.getName())) { + fileUploaded = true; + break; + } + } + + assertTrue(fileUploaded); + } + + @Test(expected = DataGridFileAlreadyExistsException.class) + public void testExistingFileTransfer() throws DataGridException { + MockMultipartFile file = new MockMultipartFile(TEST_FILE_NAME, "Hello World".getBytes()); + + us.upload(file, targetPath, false, false, "", RESOURCE, false); + + // should raise an exception + us.upload(file, targetPath, false, false, "", RESOURCE, false); + } +} diff --git a/packaging/src/emc-metalnx-services/src/test/resources/test-core-context.xml b/packaging/src/emc-metalnx-services/src/test/resources/test-core-context.xml new file mode 100755 index 000000000..01bcc804c --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/resources/test-core-context.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/test/resources/test-core-jpa.xml b/packaging/src/emc-metalnx-services/src/test/resources/test-core-jpa.xml new file mode 100755 index 000000000..9917b36bd --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/resources/test-core-jpa.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + ${hibernate.dialect} + ${hibernate.format_sql} + ${hibernate.show_sql} + ${hibernate.hbm2ddl.auto} + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/test/resources/test-services-context.xml b/packaging/src/emc-metalnx-services/src/test/resources/test-services-context.xml new file mode 100755 index 000000000..6e4467b35 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/resources/test-services-context.xml @@ -0,0 +1,71 @@ + + + + + + + + classpath:/test.metalnx.properties + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/test/resources/test_bam_file.bam b/packaging/src/emc-metalnx-services/src/test/resources/test_bam_file.bam new file mode 100755 index 000000000..2ccf43203 Binary files /dev/null and b/packaging/src/emc-metalnx-services/src/test/resources/test_bam_file.bam differ diff --git a/packaging/src/emc-metalnx-services/src/test/resources/test_illumina_file_SSU.tar b/packaging/src/emc-metalnx-services/src/test/resources/test_illumina_file_SSU.tar new file mode 100755 index 000000000..b8ac7bd83 Binary files /dev/null and b/packaging/src/emc-metalnx-services/src/test/resources/test_illumina_file_SSU.tar differ diff --git a/packaging/src/emc-metalnx-services/src/test/resources/test_jpg_file.jpg b/packaging/src/emc-metalnx-services/src/test/resources/test_jpg_file.jpg new file mode 100755 index 000000000..9699d3327 Binary files /dev/null and b/packaging/src/emc-metalnx-services/src/test/resources/test_jpg_file.jpg differ diff --git a/packaging/src/emc-metalnx-services/src/test/resources/test_manifest_xml.xml b/packaging/src/emc-metalnx-services/src/test/resources/test_manifest_xml.xml new file mode 100755 index 000000000..0db5f6262 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/resources/test_manifest_xml.xml @@ -0,0 +1,699 @@ + + + + + + + E_coli03 + + + 020.raw + file://C:/Xcalibur/data/john/opd1_2runs_2mods + 2.0 + + + + + + + + + LCQ Deca XP + + + + + + + + + + + + + + + + + Bioworks Browser + 3.3 + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAQK27W0AAAADA8x1cQAAAAECrcVxAAAAAQLxEXkAAAABAbN9hQAAAAADJoWJAAAAAgKC6YkAAAACA+WRkQAAAAIA422RAAAAAAOf6ZEAAAAAA+lRoQAAAAID132lAAAAAQLVia0AAAAAAKuFrQAAAAID9+G1AAAAAgFP8bkAAAAAAiwNwQAAAAIAnH3BAAAAAAAk2cEAAAAAAKEZwQAAAAAAsVHBAAAAAgNxgcEAAAACA4ctwQAAAAABAK3FAAAAAADBPcUAAAABAh2FxQAAAAMB3bHFAAAAAAPR8cUAAAAAA74ZxQAAAAEC7kXFAAAAAQCDfcUAAAACASypyQAAAAICNf3JAAAAAAJuRckAAAABApJ5yQAAAAICMEXNAAAAAgBwmc0AAAACAiDBzQAAAAAD7ynRAAAAAgAeIdUAAAACAo552QAAAAIDW+nZAAAAAwCwcd0AAAADAhCl3QAAAAIAPiXdAAAAAwOCvd0AAAABAvOt4QAAAAMBFpHlA + + + AAAAAIDlx0AAAAAAyBMLQQAAAADgWelAAAAAAIC550AAAAAAgD7TQAAAAAAc0D5BAAAAAAhUAUEAAAAAYEjqQAAAAACMqjdBAAAAAHAu8UAAAAAAQPLYQAAAAAAAQNFAAAAAAADv5kAAAAAA4PfgQAAAAADg2fRAAAAAAACw10AAAAAAAMLnQAAAAAAA9dVAAAAAAAiHAEEAAAAAfBwXQQAAAAAolglBAAAAAGAD40AAAAAAYAftQAAAAADAodJAAAAAAICq0kAAAAAAAJ/zQAAAAAAKFENBAAAAAJB2DkEAAAAAAAAAQAAAAABAEOtAAAAAAIDw3kAAAAAAAGbGQAAAAABAhNJAAAAAAIDU0EAAAAAAAGu3QAAAAAAAF/FAAAAAAIBe2UAAAAAAAAAYQAAAAACQ6/NAAAAAAABS2UAAAAAAAMLUQAAAAAAwD/hAAAAAAPCs80AAAAAAgKHVQAAAAAAAbOdAAAAAAIBQ0EAAAAAAyHEKQQAAAAAAE7dA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAQHcQdEAAAADAgXJ1QAAAAIDxsHVAAAAAwDpNekAAAAAAL2B6QAAAAMC1oXtAAAAAgMOve0AAAACAp619QAAAAAA6IYBAAAAAACJfgEAAAACAI62AQAAAAIAV+IBAAAAAAH5fgUAAAAAA1pCBQAAAAAACt4FAAAAAgF9PgkAAAAAARciDQAAAAICk0INAAAAAgNLrg0AAAACAkCGEQAAAAACvL4RAAAAAwMo0hEAAAACAmm+EQAAAAMCmd4RAAAAAAMTIhEAAAAAARs+EQAAAAMBg1oRAAAAAwJYvhUAAAACAPG2FQAAAAABSeoVAAAAAAHVRh0AAAAAAuVeHQAAAAABSr4dAAAAAgIW0h0AAAAAAr8CHQAAAAIB28IdAAAAAAE72h0AAAAAAkQCIQAAAAABKOIhAAAAAgOVPiEAAAACA2VeIQAAAAIBVYIhAAAAAAISyiEAAAACA8TWKQAAAAADXcYpAAAAAgMPOikAAAABA69OKQAAAAEDlG4tAAAAAwENvi0AAAABAOXiLQAAAAIDgfotAAAAAwMbJi0AAAACACtCLQAAAAACw2ItAAAAAwKDfi0AAAABASk+OQAAAAIAZ8I5AAAAAQMn4jkAAAACA4P+OQAAAAEDUfo9AAAAAgHNHkEAAAACAtsmQQAAAAACkE5FAAAAAgHgzkUAAAADAezaRQAAAAMCXQZpAAAAAgObvmkA= + + + AAAAAAAyr0AAAAAAACS7QAAAAAAAkKVAAAAAAAATu0AAAAAAABiNQAAAAAAAasxAAAAAAAB8ukAAAAAAgHLSQAAAAAAA3qNAAAAAAADIwUAAAAAAACqlQAAAAAAAOMVAAAAAAABKxEAAAAAAADKpQAAAAAAAhJxAAAAAAACgq0AAAAAAAMyVQAAAAAAAHqtAAAAAAMAO1UAAAAAAAICdQAAAAACAjdRAAAAAAAAAAEAAAAAAAJO8QAAAAAAAdL5AAAAAAAAwsUAAAAAAAPfnQAAAAABAIdpAAAAAAMAa4EAAAAAAAOKuQAAAAAAAhJhAAAAAAIBWxEAAAAAAANvEQAAAAAAASdpAAAAAAAAA8D8AAAAAAGCWQAAAAAAASd5AAAAAAIBb0EAAAAAAAIS0QAAAAAAAtLBAAAAAAMAK+kAAAAAAAMzKQAAAAACAy85AAAAAAECH1EAAAAAAAESkQAAAAACAksNAAAAAAEBe0EAAAAAAAADwPwAAAAAAAL1AAAAAALj9A0EAAAAAwI7aQAAAAAAASsxAAAAAAACHx0AAAAAAwBYDQQAAAAAA3OFAAAAAAIDozEAAAAAAAPXYQAAAAAConyBBAAAAAHB2/kAAAAAAwLjSQAAAAACA5dhAAAAAAABiuEAAAAAAAA+6QAAAAAAAOLNAAAAAAADyu0AAAAAAAKStQAAAAAAAebRAAAAAAABUm0A= + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAQFxTekAAAADAEl56QAAAAACcTIBAAAAAgLL4gEAAAABAPEKCQAAAAMDnNoRAAAAAgMl4hEAAAAAAk36EQAAAAMBfzoRAAAAAgCIRhUAAAADAhbGGQAAAAABbQIdAAAAAgI9Oh0AAAAAAwVmHQAAAAMAolodAAAAAgLagh0AAAACASrOHQAAAAABe8IdAAAAAAFH3h0AAAACAmVCIQAAAAABXcIpAAAAAAPF3ikAAAACAHLeKQAAAAACNyopAAAAAANHRikAAAABALNuKQAAAAEBZDotAAAAAAH4Yi0AAAACAIXCLQAAAAMAZeYtAAAAAgKn4jUAAAACAYS6OQAAAAICCOY5AAAAAQAqQjkAAAAAA4ZaOQAAAAEAT8o5AAAAAwDX5jkAAAABAlv+OQAAAAAAxOJBAAAAAAKJykEAAAABAtLeQQAAAAEDE15BAAAAAAMPdkEAAAADAagiRQAAAAIC+C5FAAAAAAMUQkUAAAAAAPEeSQAAAAMBzlZJAAAAAAAKYkkAAAABAmpuSQAAAAIBTn5JAAAAAgD7lkkAAAACALR+UQA== + + + AAAAAABcr0AAAAAAAAuwQAAAAAAAYrtAAAAAAACIhkAAAAAAALSpQAAAAAAA4KRAAAAAAAC9xEAAAAAAAHCqQAAAAABAItpAAAAAAIC4w0AAAAAAACCZQAAAAACAlsNAAAAAAACpw0AAAAAAAMW6QAAAAAAAk7tAAAAAAIAxw0AAAAAAANKnQAAAAACA4+FAAAAAAIDYwEAAAAAAAHnQQAAAAADAgtdAAAAAAABy0kAAAAAAAKOwQAAAAAAAl79AAAAAAACBskAAAAAAAGa1QAAAAAAgu+JAAAAAAADVyUAAAAAAIOj2QAAAAAAAxcJAAAAAAABCo0AAAAAAAJa+QAAAAAAAfuVAAAAAADBMDEEAAAAAQDvmQAAAAABIVwBBAAAAAIBJy0AAAAAAAIDNQAAAAAAAUqVAAAAAAAABtEAAAAAAAPuyQAAAAAAgQOJAAAAAAAChxkAAAAAAlA8VQQAAAACA3NpAAAAAAIBXw0AAAAAAALWxQAAAAADgPulAAAAAACzUGEEAAAAAwIz1QAAAAABAJtxAAAAAAIBUykAAAAAAADSmQA== + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAQBfjW0AAAACArihcQAAAAEDrUVxAAAAAgCR+XEAAAAAAZjteQAAAAEATUWJAAAAAgEWhYkAAAACAf75iQAAAAMC50mJAAAAAgKTUY0AAAAAAoLVkQAAAAIBu2GRAAAAAwEP2ZEAAAACAB6VrQAAAAEB8EHBAAAAAgD4hcEAAAAAAfixwQAAAAEAaPHBAAAAAQMNJcEAAAAAAr1dwQAAAAIBxYXFAAAAAAAxucUAAAACAD31xQAAAAMAzXnJAAAAAgLdqckAAAACAlopyQAAAAIAsnHJAAAAAAFK9ckAAAACA/np0QAAAAADVt3RAAAAAgKNudUAAAADActd1QAAAAEDWGHZAAAAAgBT6dkAAAADAhRp3QAAAAABxUXdAAAAAQKCTd0AAAACAVI55QA== + + + AAAAAIBSxkAAAAAAAKvgQAAAAAAAABRAAAAAAMBC4kAAAAAAAF3MQAAAAAAAT7RAAAAAAFBoQEEAAAAAgIvmQAAAAAAAACJAAAAAAIDgx0AAAAAAkH38QAAAAAA+Li1BAAAAAICkzUAAAAAAwKbdQAAAAACAYeFAAAAAAICrwkAAAAAAgN/zQAAAAAC4vApBAAAAABwVF0EAAAAAMNr4QAAAAABgyghBAAAAAJToNEEAAAAAPGIRQQAAAACAPNFAAAAAAMCv1UAAAAAAQCv9QAAAAACQZvVAAAAAAABh1EAAAAAAAA2/QAAAAACAH9VAAAAAAICf70AAAAAAwFXmQAAAAACAX8xAAAAAALBB9kAAAAAAgK/IQAAAAACAQMNAAAAAAIAb10AAAAAAgE/kQA== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAAF9BeUAAAACAI0t6QAAAAIC7UIBAAAAAgOWwgEAAAAAAPfaAQAAAAEBdAIFAAAAAgGtXgUAAAABA/XSBQAAAAEDSr4FAAAAAwFDHg0AAAACA1c+DQAAAAICsLYRAAAAAgAFxhEAAAACAmYGEQAAAAADllIRAAAAAgCqphEAAAAAAa9CEQAAAAIAOGYVAAAAAQHlIh0AAAACA3k+HQAAAAIBLWIdAAAAAANyAh0AAAACAjZCHQAAAAADHnodAAAAAgJezh0AAAACAr9aHQAAAAIB28IdAAAAAgHL3h0AAAAAA3QGIQAAAAACZF4hAAAAAgMpRiEAAAADANImIQAAAAIDPGYpAAAAAgGMuikAAAADAgG2KQAAAAIAHeYpAAAAAgMTPikAAAACAag+LQAAAAADRGItAAAAAACIei0AAAADANyiLQAAAAEC2YItAAAAAgNZvi0AAAABAp3eLQAAAAAABm4tAAAAAQDHvjUAAAACAvi+OQAAAAABzOY5AAAAAQN9MjkAAAACA2FeOQAAAAIC+j45AAAAAgHyWjkAAAADAzJyOQAAAAIC08I5AAAAAgJb4jkAAAACAbYeQQAAAAIDFqJBAAAAAgEq4kEAAAABAnc+QQAAAAMCZ15BAAAAAwEjckEAAAAAABd+QQAAAAADUB5FAAAAAABQMkUAAAAAABRCRQAAAAAC/KpFAAAAAgFo8kUAAAACA5UmRQAAAAACHR5JAAAAAAK9LkkAAAAAA15eSQAAAAMDdm5JAAAAAgNeekkAAAACAK9+SQAAAAIAFq5RAAAAAgMGwlEA= + + + AAAAAMCC0EAAAAAAAKS6QAAAAACA7MJAAAAAAACJt0AAAAAAALCcQAAAAACAEcRAAAAAAADslEAAAAAAABS1QAAAAAAAgbJAAAAAAAB8kkAAAAAAANCmQAAAAAAA6INAAAAAAABQm0AAAAAAACSqQAAAAAAApq5AAAAAAAA8rUAAAAAAAHLMQAAAAAAAmqBAAAAAAAAuqEAAAAAAAD+8QAAAAAAAetxAAAAAAAAEmkAAAAAAgJvLQAAAAAAApKZAAAAAAAAyoEAAAAAAAICKQAAAAACAyMZAAAAAAADXuEAAAAAAgKnDQAAAAAAAFqdAAAAAAIBPwEAAAAAAAGWwQAAAAACAOcJAAAAAAAB3uEAAAAAAANW2QAAAAAAAlqdAAAAAAABxwUAAAAAAICThQAAAAACA4OFAAAAAAAAAAEAAAAAAAOm1QAAAAAAAYqRAAAAAACA06EAAAAAAAOq2QAAAAAAAWqRAAAAAAIC0zEAAAAAAgG7KQAAAAAAACMBAAAAAAAA4nUAAAAAAABG7QAAAAAAwaQlBAAAAAMBx40AAAAAAAADwPwAAAADwzQZBAAAAAABbsUAAAAAAwM3dQAAAAAAA1JhAAAAAAAD4lkAAAAAAAOCNQAAAAABQkvFAAAAAAACZ0EAAAAAAANfOQAAAAACQvhFBAAAAAMAN7UAAAAAAAJqoQAAAAAAA579AAAAAAACCr0AAAAAAAMiNQAAAAACA38pAAAAAAACEr0AAAAAA4rwgQQAAAACgU/NAAAAAAEDE0UAAAAAAAA6lQAAAAAAA2LpAAAAAAABgskA= + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAwAkedEAAAABA1XF1QAAAAMCrhHtAAAAAgG+qe0AAAABAslR8QAAAAMDFXnxAAAAAwGDOfEAAAAAAJrGAQAAAAEDd3oBAAAAAgP8OgUAAAAAASlWBQAAAAEBbroFAAAAAgKK2gUAAAACAPa6CQAAAAICxkINAAAAAAMwvhEAAAACAJzqEQAAAAAC6b4RAAAAAwJiQhEAAAABA98+EQAAAAEC614RAAAAAwIkMhUAAAABAWS+FQAAAAABI0IVAAAAAAKlLh0AAAACAl5+HQAAAAAD+sYdAAAAAgCXrh0AAAAAAqvCHQAAAAIDO+IdAAAAAALL/h0AAAAAA/DeIQAAAAADTPohAAAAAAHRKiEAAAAAArVCIQAAAAIBiWYhAAAAAAPKViEAAAACACbGIQAAAAICGuIhAAAAAgFjWiEAAAACAg/iIQAAAAECVwYpAAAAAACLQikAAAACAm9iKQAAAAEAKGItAAAAAwKcui0AAAAAA3mCLQAAAAIC9ZotAAAAAAHhvi0AAAABAZHeLQAAAAACJgYtAAAAAABGJi0AAAADAuKOLQAAAAACXqotAAAAAQIvKi0AAAABAhtCLQAAAAMDl2ItAAAAAAGIAjEAAAAAAUk+OQAAAAIB8V45AAAAAAFhsjkAAAADA4O+OQAAAAECK+I5AAAAAwNj/jkAAAAAAYX2PQAAAAABJh49AAAAAABSPj0AAAADAlsiQQAAAAIDA95BAAAAAgHE4kUAAAAAAqYuRQAAAAMALk5FAAAAAgOaVkUAAAABA0DKcQAAAAACHi55A + + + AAAAAAC2okAAAAAAAMCSQAAAAAAA4ItAAAAAAAAojEAAAAAAAIylQAAAAAAAbqZAAAAAAAAKpkAAAAAAgCfPQAAAAAAAyJVAAAAAAIBM0kAAAAAAgDvAQAAAAAAA0KNAAAAAAACCykAAAAAAAGCLQAAAAAAAcK5AAAAAAIBJ10AAAAAAADy5QAAAAAAA2bRAAAAAAADor0AAAAAAoMfnQAAAAACAJ8FAAAAAAABYpkAAAAAAALW4QAAAAACAwMJAAAAAAABUpkAAAAAAAA3EQAAAAACAcclAAAAAAAB6qkAAAAAAgKLWQAAAAAAATcpAAAAAAACwsUAAAAAAAOylQAAAAAAAOLVAAAAAAACiw0AAAAAAAPPpQAAAAACAxuBAAAAAAACJuUAAAAAAgBDVQAAAAAAA0rhAAAAAAABFtUAAAAAAAKyrQAAAAAAAbLNAAAAAAADsyUAAAAAAgDbGQAAAAAAA6rZAAAAAAADImEAAAAAAAEKiQAAAAAAAtLhAAAAAAKjzCEEAAAAAEPTxQAAAAAAAsMdAAAAAAADIrEAAAAAAAPjKQAAAAAAAh7FAAAAAAABIrkAAAAAA8B8QQQAAAABA5NlAAAAAAABAj0AAAAAAwA7ZQAAAAAAA9rhAAAAAAACwqkAAAAAAiuAiQQAAAABw8/1AAAAAAAArykAAAAAAAHzSQAAAAAAA1r1AAAAAAACIi0AAAAAAALa3QAAAAAAAKKFAAAAAAABAhkAAAAAAAHCSQAAAAAAAqdBAAAAAAAD6p0AAAAAAQB3SQAAAAAAAerNA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAQEOxdkAAAACASSB7QAAAAMBZoXtAAAAAQImxe0AAAACA3p2AQAAAAIAsp4BAAAAAgCivgEAAAACAQBiBQAAAAAB8PoFAAAAAAAlOgUAAAAAABVeBQAAAAABY5oFAAAAAgEfQg0AAAABAER2EQAAAAABLMIRAAAAAAFNvhEAAAAAAfHmEQAAAAIDHw4RAAAAAQMLPhEAAAAAA79SEQAAAAIAsFIVAAAAAAHszhUAAAAAA9lKFQAAAAAD1sYZAAAAAwHPPhkAAAACAitmGQAAAAAAvTYdAAAAAgK5zh0AAAACA/Y6HQAAAAICOmYdAAAAAACuyh0AAAAAAT7iHQAAAAMAx74dAAAAAAEL3h0AAAAAACv+HQAAAAACvRIhAAAAAQJlOiEAAAACA3FeIQAAAAIBylohAAAAAQEbdiEAAAAAAq2+KQAAAAIAM1opAAAAAAEcOi0AAAAAAZhmLQAAAAEDlMItAAAAAQGNwi0AAAAAAaneLQAAAAAA8g4tAAAAAAPfPi0AAAADAKtiLQAAAAEBV3YtAAAAAgMpwjEAAAABAd1CNQAAAAIBu7Y1AAAAAgKryjUAAAADAUUKOQAAAAIAdTo5AAAAAgPOPjkAAAADA8ZeOQAAAAIASn45AAAAAgEupjkAAAACAkeuOQAAAAADW8I5AAAAAALT4jkAAAABA5wKPQAAAAICyCI9AAAAAAIY5j0AAAAAA9HmQQAAAAIBrt5BAAAAAgDu6kEAAAACAdQWRQAAAAMDkB5FAAAAAgLUMkUAAAACA9RCRQAAAAACfRJFAAAAAgMqckUAAAABAKsWSQAAAAIAQyZJAAAAAgNnPkkAAAACAaCCTQA== + + + AAAAAABXuUAAAAAAALyeQAAAAAAACcpAAAAAAACbtEAAAAAAAHe2QAAAAAAAjbBAAAAAAICbw0AAAAAAAEO7QAAAAACA9sFAAAAAAACj3EAAAAAAAPiJQAAAAAAAhJlAAAAAAMAz0UAAAAAAAIKwQAAAAACAPcRAAAAAAIBQyEAAAAAAAKLPQAAAAAAAU7JAAAAAAICi1EAAAAAAAAAQQAAAAAAAAstAAAAAAAAgrEAAAAAAgAfAQAAAAAAAwKJAAAAAAAAetkAAAAAAAGieQAAAAAAAvrxAAAAAAACOqEAAAAAAAOWxQAAAAAAAmJdAAAAAAICOxUAAAAAAAKm+QAAAAABA+eBAAAAAAADkqEAAAAAAANnyQAAAAAAAS7RAAAAAAEDr9EAAAAAAgJzBQAAAAAAA0KNAAAAAAAAslkAAAAAAgFjMQAAAAAAAK8pAAAAAAMB13UAAAAAAQOrXQAAAAAAA08VAAAAAAKDH+0AAAAAAgCrsQAAAAACAUclAAAAAADD//UAAAAAAwA7hQAAAAAAAAPA/AAAAAACsoEAAAAAAAFzGQAAAAACAQ8RAAAAAAAAA8D8AAAAAAODFQAAAAACA/tRAAAAAANgsAkEAAAAA4MjgQAAAAADAtNdAAAAAAAAOpUAAAAAAIJbhQAAAAACI0RdBAAAAAKAs50AAAAAAADu4QAAAAAAAgH9AAAAAAABElUAAAAAAAHW0QAAAAABgQfdAAAAAAAC+qEAAAAAAAHjPQAAAAABCwyRBAAAAACgsAUEAAAAAADfIQAAAAAAAKqpAAAAAAACYnUAAAAAAgO/AQAAAAACAIsBAAAAAAAC3vkAAAAAAAOKnQA== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAwHfRhEAAAACAlruGQAAAAADV4YhAAAAAQNf2i0AAAABApg6MQAAAAAAKKYxAAAAAAEdAjEAAAABA6V6NQAAAAECv4I1AAAAAwKdJjkAAAABAF2GOQAAAAED1kI9AAAAAgCgfkEA= + + + AAAAAICMwkAAAAAAADCpQAAAAAAA0IBAAAAAAAA/xkAAAAAAQIjQQAAAAAAATJNAAAAAAAA0m0AAAAAAAPicQAAAAAAAnL1AAAAAAAC2t0AAAAAAALCGQAAAAAAAfJ1AAAAAAACAi0A= + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAgKhDX0AAAAAA05lfQAAAAMDAZGJAAAAAAGOkYkAAAAAAUWNkQAAAAAD7lGRAAAAAQNBgZkAAAAAAV61pQAAAAADkGWpAAAAAAG4MbEAAAABAKRxuQAAAAECSJ29AAAAAAJ7tb0AAAAAAPHVwQAAAAIDGf3BAAAAAgJ8ocUAAAAAAv25xQAAAAABaDXJAAAAAACYtckAAAACAsU9yQAAAAMDCXnJAAAAAgMR5ckAAAACANpdyQAAAAECar3JAAAAAQNrPckAAAAAAUOlyQAAAAMBv/HJAAAAAgHJYc0AAAAAAk9hzQAAAAEBPAHRAAAAAQAQydEAAAAAA9mt0QAAAAIAvwnRAAAAAgIfNdEAAAACAV/N0QAAAAADaFHVAAAAAQIEzdUAAAAAAnVR1QAAAAACHcXVAAAAAgJmTdUAAAACAnLZ1QAAAAIAt3HVAAAAAgIzpdUAAAACAkxR2QAAAAEDMTnZAAAAAgHpbdkAAAABAb5x2QAAAAECGrHZAAAAAQMC4dkAAAAAAJcp2QAAAAEBD2XZAAAAAQCfydkAAAADAJkJ3QAAAAIDzT3dAAAAAQL6Ad0AAAABAQZd3QAAAAABCrndAAAAAgPfJd0AAAACAmdl3QAAAAEAd7XdAAAAAAMn2d0AAAADANAp4QAAAAECYFHhAAAAAwK0xeEAAAACART54QAAAAMACTnhAAAAAgA9heEAAAADAGpJ4QAAAAECWynhAAAAAAFbeeEAAAADAK+t4QAAAAMC4GHlAAAAAgGoleUAAAABAgkV5QAAAAEC+WHlAAAAAAINqeUAAAADAYtR5QAAAAEBWGHpAAAAAwDZGekAAAACAtW96QAAAAMCehXpAAAAAgJapekAAAACAo4x7QAAAAIC3sntAAAAAwNqRfEAAAABApjCFQAAAAMDeaoVA + + + AAAAAACglEAAAAAAACSXQAAAAAAA6JBAAAAAAADSokAAAAAAAJKjQAAAAAAAcJBAAAAAAACcmEAAAAAAANa5QAAAAAAA4MRAAAAAAABGxkAAAAAAAHKxQAAAAAAAPrhAAAAAAADImUAAAAAAAMitQAAAAAAAsIlAAAAAAABzsUAAAAAAADW2QAAAAAAAqbJAAAAAAAAQkUAAAAAAAASiQAAAAAAAvqBAAAAAAAAFs0AAAAAAAADGQAAAAAAASqJAAAAAAABUnUAAAAAAAOKlQAAAAAAAfLdAAAAAAADYnEAAAAAAAO+2QAAAAAAAmLRAAAAAAADFsEAAAAAAAOyyQAAAAAAAcq5AAAAAAABIyUAAAAAAgDHOQAAAAAAADJdAAAAAAACpsEAAAAAAAE6jQAAAAAAAoHhAAAAAAIBlwEAAAAAAABSZQAAAAAAAwbBAAAAAAAD8ukAAAAAAADOyQAAAAAAA275AAAAAAADipUAAAAAAABCsQAAAAAAA8bVAAAAAAADaokAAAAAAALu8QAAAAAAA0rxAAAAAAAAGpUAAAAAAAOCYQAAAAAAAEK1AAAAAAABAdkAAAAAAAMXCQAAAAAAARsBAAAAAAABwxUAAAAAAABa+QAAAAAAA07lAAAAAAMBh00AAAAAAAOyQQAAAAAAArKVAAAAAAAAWu0AAAAAAAGC1QAAAAACAF89AAAAAAADvuUAAAAAAADyoQAAAAAAAs7pAAAAAAABuxEAAAAAAADK1QAAAAAAA+qRAAAAAAADIrEAAAAAAAJyjQAAAAAAAgL5AAAAAAAD+uEAAAAAAAMKvQAAAAAAAHqZAAAAAAACftkAAAAAAAIm8QAAAAAAA4sNAAAAAAAAcoUAAAAAAACynQAAAAAAAG7BAAAAAAAAspEAAAAAAAGLAQAAAAAAAxLJA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AAAAALJcZkAAAAAAniVoQAAAAIDC1GtAAAAAwIoebEAAAAAA5F1sQAAAAEAuVW1AAAAAgJ/EbUAAAADAUOFtQAAAAEAH7G9AAAAAAIC6ckAAAAAANhdzQAAAAEDqIHNAAAAAQHddc0AAAACA0g50QAAAAEAXhHRAAAAAAIrPdEAAAAAAt+Z0QAAAAADdaHVAAAAAwGR6dUAAAACASaZ1QAAAAADJJ3ZAAAAAQN9SdkAAAAAAVGB2QAAAAIC6anZAAAAAgKR5dkAAAAAAJ452QAAAAIAN+nZAAAAAgPMmd0AAAADAzlt3QAAAAEBGi3dAAAAAgMSWd0AAAADAkbl3QAAAAICe13dAAAAAAEECeEAAAADA+Rt4QAAAAMCyJXhAAAAAAHx6eEAAAABAzMV4QAAAAMB34nhAAAAAACz1eEAAAABA2CV5QAAAAMCYOXlAAAAAQDRJeUAAAACACld5QAAAAECe5XlAAAAAwJr2eUAAAAAACA16QAAAAID2PHpAAAAAAARKekAAAACAelp6QAAAAID+aXpAAAAAgON2ekAAAADA35B6QAAAAEADrHpAAAAAgOnAekAAAAAALc56QAAAAEBG3npAAAAAQB/tekAAAABApht7QAAAAACuO3tAAAAAgB5fe0AAAADAHIJ7QAAAAMCemXtAAAAAAOmme0AAAAAADrN7QAAAAIBGw3tAAAAAgPTNe0AAAAAAU9l7QAAAAAA77XtAAAAAwJlgfEAAAACAUst8QAAAAECl2XxAAAAAwBHufUAAAAAAFhN+QA== + + + AAAAAADUmkAAAAAAAMShQAAAAAAAsrhAAAAAAABruEAAAAAAADKgQAAAAAAAzLlAAAAAAIA4wEAAAAAAACqrQAAAAAAAIJ1AAAAAAABpukAAAAAAAGXEQAAAAAAAAPA/AAAAAAAwt0AAAAAAAAydQAAAAAAA2rFAAAAAAABgn0AAAAAAAMCTQAAAAAAAILdAAAAAAAAzsEAAAAAAABG1QAAAAAAA5JpAAAAAAEBx8kAAAAAAUJgIQQAAAADO2yRBAAAAALhKBEEAAAAAABK+QAAAAAAARKRAAAAAAACNx0AAAAAAACXFQAAAAACA9sBAAAAAAIC9y0AAAAAAAPm6QAAAAACAjsVAAAAAAABmpEAAAAAAgMPDQAAAAAAAAPA/AAAAAAAvxEAAAAAAALifQAAAAACARspAAAAAAIAKxEAAAAAAgCbCQAAAAACAzcJAAAAAAABMnUAAAAAAAELFQAAAAACAaNJAAAAAAMAP20AAAAAAAESoQAAAAAAAJ8BAAAAAAMCT3UAAAAAAAHjIQAAAAAAA9rhAAAAAAIA5w0AAAAAAAPbTQAAAAACAf9NAAAAAAIALwEAAAAAA5wAxQQAAAAA4vR5BAAAAAIC7xkAAAAAAAJi0QAAAAACAp91AAAAAAED60EAAAAAAAOWxQAAAAAAA0JxAAAAAAAAFxkAAAAAAANe6QAAAAAAAbMlAAAAAAIDPxEAAAAAAAIalQAAAAAAASNpAAAAAAICdxUAAAAAAAPDUQAAAAACAhshAAAAAAEC00EAAAAAAgN3XQA== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/test/resources/test_populate_file.txt b/packaging/src/emc-metalnx-services/src/test/resources/test_populate_file.txt new file mode 100755 index 000000000..f7cb56e9a --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/resources/test_populate_file.txt @@ -0,0 +1 @@ +Mlx \ No newline at end of file diff --git a/packaging/src/emc-metalnx-services/src/test/resources/test_vcf_file.vcf b/packaging/src/emc-metalnx-services/src/test/resources/test_vcf_file.vcf new file mode 100755 index 000000000..5bd322448 --- /dev/null +++ b/packaging/src/emc-metalnx-services/src/test/resources/test_vcf_file.vcf @@ -0,0 +1,23 @@ +##fileformat=VCFv4.0 +##fileDate=20090805 +##source=myImputationProgramV3.1 +##reference=1000GenomesPilot-NCBI36 +##phasing=partial +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##INFO= +##FILTER= +##FILTER= +##FORMAT= +##FORMAT= +##FORMAT= +##FORMAT= +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT NA00001 NA00002 NA00003 +20 14370 rs6054257 G A 29 PASS NS=3;DP=14;AF=0.5;DB;H2 GT:GQ:DP:HQ 0|0:48:1:51,51 1|0:48:8:51,51 1/1:43:5:.,. +20 17330 . T A 3 q10 NS=3;DP=11;AF=0.017 GT:GQ:DP:HQ 0|0:49:3:58,50 0|1:3:5:65,3 0/0:41:3 +20 1110696 rs6040355 A G,T 67 PASS NS=2;DP=10;AF=0.333,0.667;AA=T;DB GT:GQ:DP:HQ 1|2:21:6:23,27 2|1:2:0:18,2 2/2:35:4 +20 1230237 . T . 47 PASS NS=3;DP=13;AA=T GT:GQ:DP:HQ 0|0:54:7:56,60 0|0:48:4:51,51 0/0:61:2 +20 1234567 microsat1 GTCT G,GTACT 50 PASS NS=3;DP=9;AA=G GT:GQ:DP 0/1:35:4 0/2:17:2 1/1:40:3 \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/pom.xml b/packaging/src/emc-metalnx-shared/pom.xml new file mode 100755 index 000000000..ee7a0234b --- /dev/null +++ b/packaging/src/emc-metalnx-shared/pom.xml @@ -0,0 +1,221 @@ + + + + 4.0.0 + + com.emc.metalnx + emc-metalnx + 1.4.0 + + + emc-metalnx-shared + + + + com.emc.metalnx + emc-metalnx-services + ${project.version} + + + org.springframework + spring-context + + + org.springframework + spring-web + + + org.springframework + spring-webmvc + + + org.springframework.security + spring-security-web + + + org.springframework.security + spring-security-config + + + org.springframework.batch + spring-batch-core + + + + org.thymeleaf + thymeleaf + + + org.thymeleaf + thymeleaf-spring4 + + + nz.net.ultraq.thymeleaf + thymeleaf-layout-dialect + + + + + commons-fileupload + commons-fileupload + + + + commons-io + commons-io + + + + + org.seleniumhq.selenium + selenium-java + test + + + + + org.seleniumhq.selenium + selenium-htmlunit-driver + + + + javax.servlet + javax.servlet-api + + + + + com.fasterxml.jackson.core + jackson-core + + + + com.fasterxml.jackson.core + jackson-annotations + + + + com.fasterxml.jackson.core + jackson-databind + + + + + + emc-metalnx-shared + + + + src/main/resources + + static/** + **/*.html + **/*.xml + **/*.properties + + + + + + + + + maven-antrun-plugin + + + + run + + generate-sources + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.apache.maven.plugins + + + maven-antrun-plugin + + + [1.3,) + + + run + + + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/CollectionController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/CollectionController.java new file mode 100755 index 000000000..2b8cc8c5e --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/CollectionController.java @@ -0,0 +1,1119 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; + +import org.irods.jargon.core.exception.FileNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.thymeleaf.util.StringUtils; + +import com.emc.metalnx.controller.utils.LoggedUserUtils; +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.entity.DataGridResource; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.modelattribute.breadcrumb.DataGridBreadcrumb; +import com.emc.metalnx.modelattribute.collection.CollectionOrDataObjectForm; +import com.emc.metalnx.modelattribute.metadatatemplate.MetadataTemplateForm; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FavoritesService; +import com.emc.metalnx.services.interfaces.GroupBookmarkService; +import com.emc.metalnx.services.interfaces.GroupService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.PermissionsService; +import com.emc.metalnx.services.interfaces.ResourceService; +import com.emc.metalnx.services.interfaces.RuleDeploymentService; +import com.emc.metalnx.services.interfaces.UserBookmarkService; +import com.emc.metalnx.services.interfaces.UserService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@SessionAttributes({ "sourcePaths" }) +@RequestMapping(value = "/collections") +public class CollectionController { + + @Autowired + CollectionService cs; + + @Autowired + ResourceService resourceService; + + @Autowired + UserService userService; + + @Autowired + GroupService groupService; + + @Autowired + GroupBookmarkService groupBookmarkService; + + @Autowired + UserBookmarkService userBookmarkService; + + @Autowired + MetadataService metadataService; + + @Autowired + GroupBookmarkController groupBookmarkController; + + @Autowired + PermissionsService permissionsService; + + @Autowired + IRODSServices irodsServices; + + @Autowired + FavoritesService favoritesService; + + @Autowired + LoggedUserUtils loggedUserUtils; + + @Autowired + RuleDeploymentService ruleDeploymentService; + + // parent path of the current directory in the tree view + private String parentPath; + + // path to the current directory in the tree view + private String currentPath; + + // number of pages for current path + private int totalObjsForCurrentPath; + + // number of pages for current search + private int totalObjsForCurrentSearch; + + // Auxiliary structure to manage download, upload, copy and move operations + private List sourcePaths; + + // ui mode that will be shown when the rods user switches mode from admin to + // user and vice-versa + public static final String UI_USER_MODE = "user"; + public static final String UI_ADMIN_MODE = "admin"; + + public static final int MAX_HISTORY_SIZE = 10; + + private boolean cameFromMetadataSearch; + private boolean cameFromFilePropertiesSearch; + private boolean cameFromBookmarks; + + private Stack collectionHistoryBack; + private Stack collectionHistoryForward; + + // variable to save trash path for the logged user + private String userTrashPath = ""; + // saves the trash under the zone + private String zoneTrashPath = ""; + + private static final Logger logger = LoggerFactory.getLogger(CollectionController.class); + + @PostConstruct + public void init() throws DataGridException { + collectionHistoryBack = new Stack(); + collectionHistoryForward = new Stack(); + + cameFromMetadataSearch = false; + cameFromFilePropertiesSearch = false; + cameFromBookmarks = false; + + sourcePaths = new ArrayList<>(); + parentPath = ""; + currentPath = ""; + } + + /** + * Responds the collections/ request + * + * @param model + * @return the collection management template + * @throws DataGridException + */ + @RequestMapping(value = "/") + public String index(final Model model, final HttpServletRequest request, + @RequestParam(value = "uploadNewTab", required = false) final boolean uploadNewTab) + throws DataGridConnectionRefusedException { + + logger.info("index()"); + try { + sourcePaths.clear(); + + if (!cs.isPathValid(currentPath)) { + currentPath = cs.getHomeDirectyForCurrentUser(); + parentPath = currentPath; + } else if (cs.isDataObject(currentPath)) { + parentPath = currentPath.substring(0, currentPath.lastIndexOf("/") + 1); + } + + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + String uiMode = (String) request.getSession().getAttribute("uiMode"); + + if (uiMode == null || uiMode.isEmpty()) { + boolean isUserAdmin = loggedUser != null && loggedUser.isAdmin(); + uiMode = isUserAdmin ? UI_ADMIN_MODE : UI_USER_MODE; + } + + if (uiMode.equals(UI_USER_MODE)) { + model.addAttribute("homePath", cs.getHomeDirectyForCurrentUser()); + model.addAttribute("publicPath", cs.getHomeDirectyForPublic()); + } + if (uploadNewTab) { + model.addAttribute("uploadNewTab", uploadNewTab); + } + + model.addAttribute("cameFromFilePropertiesSearch", cameFromFilePropertiesSearch); + model.addAttribute("cameFromMetadataSearch", cameFromMetadataSearch); + model.addAttribute("cameFromBookmarks", cameFromBookmarks); + model.addAttribute("uiMode", uiMode); + model.addAttribute("currentPath", currentPath); + model.addAttribute("parentPath", parentPath); + model.addAttribute("resources", resourceService.findAll()); + model.addAttribute("overwriteFileOption", loggedUser != null && loggedUser.isForceFileOverwriting()); + + cameFromMetadataSearch = false; + cameFromFilePropertiesSearch = false; + cameFromBookmarks = false; + } catch (DataGridException e) { + logger.error("Could not respond to request for collections: {}", e); + model.addAttribute("unexpectedError", true); + } + + return "collections/collectionManagement"; + } + + @RequestMapping(value = "redirectFromMetadataToCollections/") + @ResponseStatus(value = HttpStatus.OK) + public void redirectFromMetadataToCollections(@RequestParam final String path) { + assignNewValuesToCurrentAndParentPath(path); + cameFromMetadataSearch = true; + } + + @RequestMapping(value = "redirectFromFavoritesToCollections/") + @ResponseStatus(value = HttpStatus.OK) + public void redirectFromFavoritesToCollections(@RequestParam final String path) { + assignNewValuesToCurrentAndParentPath(path); + } + + @RequestMapping(value = "redirectFromGroupsBookmarksToCollections/") + @ResponseStatus(value = HttpStatus.OK) + public void redirectFromGroupsBookmarksToCollections(@RequestParam final String path) { + cameFromBookmarks = true; + assignNewValuesToCurrentAndParentPath(path); + } + + @RequestMapping(value = "redirectFromUserBookmarksToCollections/") + @ResponseStatus(value = HttpStatus.OK) + public void redirectFromUserBookmarksToCollections(@RequestParam final String path) { + cameFromBookmarks = true; + assignNewValuesToCurrentAndParentPath(path); + } + + @RequestMapping(value = "redirectFromFilePropertiesToCollections/") + @ResponseStatus(value = HttpStatus.OK) + public void redirectFromFilePropertiesToCollections(@RequestParam final String path) { + assignNewValuesToCurrentAndParentPath(path); + cameFromFilePropertiesSearch = true; + } + + /** + * Get a list of resources in which an object doesn't have replicas + * + * @param model + * @return list of resources in which an object can be replicated + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "getAvailableRescForPath/") + public String getAvailableRescForPath(final Model model, @RequestParam("isUpload") final boolean isUpload) + throws DataGridConnectionRefusedException { + + Map replicasMap = null; + List resources = resourceService.findFirstLevelResources(); + + if (!isUpload) { + for (String path : sourcePaths) { + replicasMap = cs.listReplicasByResource(path); + for (DataGridResource resc : replicasMap.values()) { + if (resources.contains(resc)) { + resources.remove(resc); + } + } + } + } + model.addAttribute("resources", resources); + return "collections/collectionsResourcesForReplica"; + } + + /** + * Switches an admin from the Rods_Admin UI to the Rods_User UI and vice-versa. + * + * @param model + * @return redirects an admin user from to the new UI view mode (admin view or + * user view) + */ + @RequestMapping(value = "/switchMode/") + @ResponseStatus(value = HttpStatus.OK) + public void switchMode(final Model model, final HttpServletRequest request, + @RequestParam("currentMode") final String currentMode, final RedirectAttributes redirectAttributes) { + + // if the admin is currently seeing the Admin UI, we need to switch it + // over to the USER UI + if (currentMode.equalsIgnoreCase(UI_ADMIN_MODE)) { + request.getSession().setAttribute("uiMode", UI_USER_MODE); + } + // if the admin is currently seeing the User UI, we need to switch it + // over to the ADMIN UI + else if (currentMode.equalsIgnoreCase(UI_USER_MODE)) { + request.getSession().setAttribute("uiMode", UI_ADMIN_MODE); + } + } + + /** + * Responds the getSubdirectories request finding collections and data objects + * that exist underneath a certain path + * + * @param model + * @param path + * path to find all subdirectories and objects + * @return treeView template that renders all nodes of certain path (parent) + * @throws DataGridException + * if Metalnx cannot find collections and objects inside the path + */ + @RequestMapping(value = "/getSubDirectories/", method = RequestMethod.POST) + public String getSubDirectories(final Model model, @RequestParam("path") String path) throws DataGridException { + + // removes all ocurrences of "/" at the end of the path string + while (path.endsWith("/") && !"/".equals(path)) { + path = path.substring(0, path.lastIndexOf("/")); + } + + logger.info("Get subdirectories of {}", path); + + // put old path in collection history stack + addPathToHistory(path); + + return getCollBrowserView(model, path); + } + + /** + * Goes back in collection historic stack + * + * @param model + * @param steps + * @return treeView template that renders all nodes of certain path (parent) + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "/goBackHistory/", method = RequestMethod.POST) + public String goBackHistory(final Model model, @RequestParam("steps") final int steps) + throws DataGridException, JargonException { + if (collectionHistoryBack.size() < steps || steps < 1) { + model.addAttribute("invalidStepsBackwards", steps); + logger.info("It is not possible to go back {} steps, current stack size is {}", steps, + collectionHistoryBack.size()); + return getCollBrowserView(model, currentPath); + } + + logger.info("Going back {} steps in collection history", steps); + + // pop paths from collectionHistoryBack and push them to + // collectionHistoryForward + while (collectionHistoryForward.size() >= MAX_HISTORY_SIZE) { + collectionHistoryForward.remove(0); + } + collectionHistoryForward.push(currentPath); + for (int i = 0; i < steps - 1; i++) { + String elementHistory = collectionHistoryBack.pop(); + while (collectionHistoryForward.size() >= MAX_HISTORY_SIZE) { + collectionHistoryForward.remove(0); + } + collectionHistoryForward.push(elementHistory); + } + + return getCollBrowserView(model, collectionHistoryBack.pop()); + } + + /** + * Goes forward in collection historic stack + * + * @param model + * @param steps + * @return treeView template that renders all nodes of certain path (parent) + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "/goForwardHistory/", method = RequestMethod.POST) + public String goForwardHistory(final Model model, @RequestParam("steps") final int steps) + throws DataGridException, JargonException { + if (collectionHistoryForward.size() < steps || steps < 1) { + model.addAttribute("invalidStepsForward", steps); + return getCollBrowserView(model, currentPath); + } + + logger.info("Going {} steps forward in collection history", steps); + + // pop paths from collectionHistoryBack and push them to + // collectionHistoryForward + while (collectionHistoryBack.size() >= MAX_HISTORY_SIZE) { + collectionHistoryBack.remove(0); + } + collectionHistoryBack.push(currentPath); + for (int i = 0; i < steps - 1; i++) { + String elementHistory = collectionHistoryForward.pop(); + while (collectionHistoryBack.size() >= MAX_HISTORY_SIZE) { + collectionHistoryBack.remove(0); + } + collectionHistoryBack.push(elementHistory); + } + + return getCollBrowserView(model, collectionHistoryForward.pop()); + } + + /** + * Responds the getSubdirectories request finding collections and data objects + * that exist underneath a certain path + * + * @param model + * @param path + * @return treeView template that renders all nodes of certain path (parent) + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "/getSubDirectoriesOldTree/") + public String getSubDirectoriesOldTree(final Model model, @RequestParam("path") String path) + throws DataGridConnectionRefusedException { + + if (path.isEmpty()) { + path = "/"; + } else { + if (path.endsWith("/") && path.compareTo("/") != 0) { + path = path.substring(0, path.length() - 1); + } + } + + // The line below was modified so that only collection would be retrieved + model.addAttribute("dataGridCollectionAndDataObjectList", cs.getSubCollectionsUnderPath(path)); + + return "collections/oldTreeView :: oldTreeView"; + } + + /** + * Gets checksum, total number of replicas and where each replica lives in the + * data grid for a specific data object + * + * @param model + * @param path + * path to the data object to get checksum and replica information + * @return the template that shows the data object information + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "/info/", method = RequestMethod.POST) + public String getFileInfo(final Model model, @RequestParam("path") final String path) + throws DataGridConnectionRefusedException { + + DataGridCollectionAndDataObject dataGridObj = null; + Map replicasMap = null; + + try { + dataGridObj = cs.findByName(path); + + if (dataGridObj != null && !dataGridObj.isCollection()) { + replicasMap = cs.listReplicasByResource(path); + dataGridObj.setChecksum(cs.getChecksum(path)); + dataGridObj.setNumberOfReplicas(cs.getTotalNumberOfReplsForDataObject(path)); + dataGridObj.setReplicaNumber(String.valueOf(cs.getReplicationNumber(path))); + permissionsService.resolveMostPermissiveAccessForUser(dataGridObj, + loggedUserUtils.getLoggedDataGridUser()); + } + + } catch (DataGridConnectionRefusedException e) { + logger.error("Could not connect to the data grid", e); + throw e; + } catch (DataGridException e) { + logger.error("Could not get file info for {}", path, e); + } + + model.addAttribute("collectionAndDataObject", dataGridObj); + model.addAttribute("currentCollection", dataGridObj); + model.addAttribute("replicasMap", replicasMap); + + return "collections/collectionInfo"; + } + + /** + * Finds all collections and files existing under a certain path for a given + * group name. + * + * @param model + * @param path + * start point to get collections and files + * @param groupName + * group that all collections and files permissions will be listed + * @return + * @throws DataGridConnectionRefusedException + * @throws JargonException + * @throws FileNotFoundException + */ + @RequestMapping(value = "/getDirectoriesAndFilesForGroupForm") + public String getDirectoriesAndFilesForGroupForm(final Model model, @RequestParam("path") String path, + @RequestParam("groupName") final String groupName, + @RequestParam("retrievePermissions") final boolean retrievePermissions) + throws DataGridConnectionRefusedException, FileNotFoundException, JargonException { + if (path == null || path == "") { + path = "/"; + } + + List list = null; + list = cs.getSubCollectionsAndDataObjectsUnderPath(path); + + Set readPermissions = null; + Set writePermissions = null; + Set ownershipPermissions = null; + Set inheritPermissions = null; + + if (retrievePermissions) { + readPermissions = cs.listReadPermissionsForPathAndGroup(path, groupName); + writePermissions = cs.listWritePermissionsForPathAndGroup(path, groupName); + ownershipPermissions = cs.listOwnershipForPathAndGroup(path, groupName); + inheritPermissions = cs.listInheritanceForPath(path); + } else { + readPermissions = new HashSet(); + writePermissions = new HashSet(); + ownershipPermissions = new HashSet(); + inheritPermissions = new HashSet(); + } + + List groupBookmarks = new ArrayList(); + if (groupName.length() > 0) { + DataGridGroup group = groupService.findByGroupname(groupName).get(0); + groupBookmarks = groupBookmarkService.findBookmarksForGroupAsString(group); + } + + model.addAttribute("dataGridCollectionAndDataObjectList", list); + model.addAttribute("currentPath", path); + model.addAttribute("readPermissions", readPermissions); + model.addAttribute("writePermissions", writePermissions); + model.addAttribute("ownershipPermissions", ownershipPermissions); + model.addAttribute("inheritPermissions", inheritPermissions); + model.addAttribute("addBookmark", groupBookmarkController.getAddBookmark()); + model.addAttribute("removeBookmark", groupBookmarkController.getRemoveBookmark()); + model.addAttribute("groupBookmarks", groupBookmarks); + + return "collections/treeViewForGroupForm :: treeView"; + } + + /** + * Finds all collections existing under a certain path. + * + * @param model + * @param path + * start point to get collections and files + * @param username + * user who all collections and files permissions will be listed + * @return the template that will render the tree + * @throws DataGridConnectionRefusedException + * @throws JargonException + * @throws FileNotFoundException + */ + @RequestMapping(value = "/getDirectoriesAndFilesForUser") + public String getDirectoriesAndFilesForUser(final Model model, @RequestParam("path") final String path, + @RequestParam("username") final String username, + @RequestParam("retrievePermissions") final boolean retrievePermissions) + throws DataGridConnectionRefusedException, FileNotFoundException, JargonException { + List list = new ArrayList(); + Set readPermissions = new HashSet(); + Set writePermissions = new HashSet(); + Set ownershipPermissions = new HashSet(); + Set inheritPermissions = new HashSet(); + List userBookmarks = new ArrayList(); + + // If a string is null, empty or contains only white spaces, StringUtils + // returns true + boolean isPathEmpty = StringUtils.isEmptyOrWhitespace(path); + boolean isUsernameEmpty = StringUtils.isEmptyOrWhitespace(username); + + if (!isPathEmpty) { + // When adding a user (there is no username), we still need to be + // able to walk through the iRODS tree + list = cs.getSubCollectionsAndDataObjectsUnderPath(path); + + if (!isUsernameEmpty) { + if (retrievePermissions) { + readPermissions = cs.listReadPermissionsForPathAndUser(path, username); + writePermissions = cs.listWritePermissionsForPathAndUser(path, username); + ownershipPermissions = cs.listOwnershipForPathAndUser(path, username); + inheritPermissions = cs.listInheritanceForPath(path); + } + + List users = userService.findByUsername(username); + if (users != null && !users.isEmpty()) { + userBookmarks = userBookmarkService.findBookmarksForUserAsString(users.get(0)); + } + } + } + + model.addAttribute("dataGridCollectionAndDataObjectList", list); + model.addAttribute("currentPath", path); + model.addAttribute("readPermissions", readPermissions); + model.addAttribute("writePermissions", writePermissions); + model.addAttribute("ownershipPermissions", ownershipPermissions); + model.addAttribute("inheritPermissions", inheritPermissions); + model.addAttribute("addBookmark", new ArrayList()); + model.addAttribute("removeBookmark", new ArrayList()); + model.addAttribute("userBookmarks", userBookmarks); + + return "collections/treeViewForUserForm :: treeView"; + } + + /** + * Looks for collections or data objects that match the parameter string + * + * @param model + * @param name + * collection name that will be searched in the data grid + * @return the template that renders all collections and data objects matching + * the parameter string + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "/find/{name}") + public String listCollectionsAndDataObjects(final Model model, @PathVariable final String name) + throws DataGridConnectionRefusedException { + logger.info("Finding collections or data objects that match " + name); + + // Find collections and data objects + List dataGridCollectionAndDataObjects = cs + .searchCollectionAndDataObjectsByName(name + "%"); + model.addAttribute("dataGridCollectionAndDataObjects", dataGridCollectionAndDataObjects); + return "collections/collectionsBrowser :: treeView"; + } + + /** + * Performs the action of actually creating a collection in iRODS + * + * @param model + * @param collection + * @return if the creation of collection was successful, it returns the + * collection management template, and returns the add collection + * template, otherwise. + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "add/action/", method = RequestMethod.POST) + public String addCollection(final Model model, @ModelAttribute final CollectionOrDataObjectForm collection, + final RedirectAttributes redirectAttributes) throws DataGridConnectionRefusedException { + DataGridCollectionAndDataObject newCollection = new DataGridCollectionAndDataObject( + currentPath + '/' + collection.getCollectionName(), collection.getCollectionName(), currentPath, true); + + newCollection.setParentPath(currentPath); + newCollection.setCreatedAt(new Date()); + newCollection.setModifiedAt(newCollection.getCreatedAt()); + newCollection.setInheritanceOption(collection.getInheritOption()); + + boolean creationSucessful; + try { + creationSucessful = cs.createCollection(newCollection); + + if (creationSucessful) { + redirectAttributes.addFlashAttribute("collectionAddedSuccessfully", collection.getCollectionName()); + } + } catch (DataGridConnectionRefusedException e) { + throw e; + } catch (DataGridException e) { + logger.error("Could not create collection/data object (lack of permission): ", e.getMessage()); + redirectAttributes.addFlashAttribute("missingPermissionError", true); + } + + return "redirect:/collections/"; + } + + /** + * Performs the action of modifying a collection + * + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "modify/action", method = RequestMethod.POST) + public String modifyAction(@ModelAttribute final CollectionOrDataObjectForm collForm, + final RedirectAttributes redirectAttributes) throws DataGridException { + String previousPath = collForm.getPath(); + String parentPath = previousPath.substring(0, previousPath.lastIndexOf("/")); + String newPath = String.format("%s/%s", parentPath, collForm.getCollectionName()); + + logger.info("Modify action for " + previousPath + "/" + newPath); + boolean modificationSuccessful = cs.modifyCollectionAndDataObject(previousPath, newPath, + collForm.getInheritOption()); + + if (modificationSuccessful) { + logger.debug("Collection/Data Object {} modified to {}", previousPath, newPath); + + userBookmarkService.updateBookmark(previousPath, newPath); + groupBookmarkService.updateBookmark(previousPath, newPath); + + redirectAttributes.addFlashAttribute("collectionModifiedSuccessfully", collForm.getCollectionName()); + } + + return "redirect:/collections/"; + } + + @RequestMapping(value = "applyTemplatesToCollections/", method = RequestMethod.POST) + public String applyTemplatesToCollections(final RedirectAttributes redirectAttributes, + @ModelAttribute final MetadataTemplateForm template) throws DataGridConnectionRefusedException { + boolean templatesAppliedSuccessfully = applyTemplatesToPath(template); + redirectAttributes.addFlashAttribute("templatesAppliedSuccessfully", templatesAppliedSuccessfully); + return "redirect:/collections/"; + } + + private boolean applyTemplatesToPath(final MetadataTemplateForm template) + throws DataGridConnectionRefusedException { + boolean allMetadataAdded = true; + List attributes = template.getAvuAttributes(); + List values = template.getAvuValues(); + List units = template.getAvuUnits(); + + if (attributes == null || values == null || units == null) { + return false; + } + + for (int i = 0; i < attributes.size(); i++) { + String attr = attributes.isEmpty() ? "" : attributes.get(i); + String val = values.isEmpty() ? "" : values.get(i); + String unit = units.isEmpty() ? "" : units.get(i); + for (String path : template.getPaths()) { + boolean isMetadadaAdded = metadataService.addMetadataToPath(path, attr, val, unit); + if (!isMetadadaAdded) { + allMetadataAdded = false; + } + } + } + + return allMetadataAdded; + } + + /* + * **************************************************************************** + * ************************ USER COLLECTION CONTROLLER ************************ + * **************************************************************************** + */ + + /** + * Responds the collections/home request + * + * @param model + * @return the collection management template + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "/home/") + public String homeCollection(final Model model) throws DataGridException { + // cleaning session variables + sourcePaths.clear(); + currentPath = cs.getHomeDirectyForCurrentUser(); + parentPath = currentPath; + return "redirect:/collections/"; + } + + /** + * Responds the collections/public request + * + * @param model + * @return the collection management template + */ + @RequestMapping(value = "/public/") + public String publicCollection(final Model model) throws DataGridException { + // cleaning session variables + sourcePaths.clear(); + + currentPath = cs.getHomeDirectyForPublic(); + parentPath = currentPath; + + model.addAttribute("publicPath", currentPath); + model.addAttribute("currentPath", currentPath); + model.addAttribute("parentPath", parentPath); + model.addAttribute("homePath", cs.getHomeDirectyForCurrentUser()); + model.addAttribute("resources", resourceService.findAll()); + + return "collections/collectionManagement"; + } + + /** + * Responds the collections/trash request + * + * @param model + * @return the collection management template + * @throws DataGridException + */ + @RequestMapping(value = "/trash/") + public String trashCollection(final Model model) throws DataGridException { + // cleaning session variables + sourcePaths.clear(); + + if (userTrashPath == null || userTrashPath.equals("")) { + userTrashPath = String.format("/%s/trash/home/%s", irodsServices.getCurrentUserZone(), + irodsServices.getCurrentUser()); + } + currentPath = userTrashPath; + parentPath = currentPath; + + model.addAttribute("currentPath", currentPath); + model.addAttribute("parentPath", parentPath); + model.addAttribute("publicPath", cs.getHomeDirectyForPublic()); + model.addAttribute("homePath", cs.getHomeDirectyForCurrentUser()); + model.addAttribute("resources", resourceService.findAll()); + + return "collections/collectionManagement"; + } + + @RequestMapping(value = "/getBreadCrumbForObject/") + public String getBreadCrumbForObject(final Model model, @RequestParam("path") String path) + throws DataGridConnectionRefusedException { + if (path.isEmpty()) { + path = currentPath; + } else { + if (path.endsWith("/") && path.compareTo("/") != 0) { + path = path.substring(0, path.length() - 1); + } + if (!path.equals(currentPath) + && (collectionHistoryBack.isEmpty() || !currentPath.equals(collectionHistoryBack.peek()))) { + while (collectionHistoryBack.size() >= MAX_HISTORY_SIZE) { + collectionHistoryBack.remove(0); + } + collectionHistoryBack.push(currentPath); + if (!collectionHistoryForward.isEmpty()) { + collectionHistoryForward.clear(); + } + } + currentPath = path; + } + + setBreadcrumbToModel(model, path); + return "collections/collectionsBreadCrumb"; + } + + /* + * ***************************************************************************** + * ******************************** VALIDATION ********************************* + * ***************************************************************************** + */ + + /** + * Validates a collection name in iRODS + * + * @return True, if the collection name can be used. False, otherwise. + * @throws DataGridConnectionRefusedException + */ + @ResponseBody + @RequestMapping(value = "isValidCollectionName/{newObjectName}/", method = RequestMethod.GET, produces = { + "text/plain" }) + public String isValidCollectionName(@PathVariable final String newObjectName) throws DataGridException { + String rc = "true"; + String newPath = String.format("%s/%s", currentPath, newObjectName); + + try { + cs.findByName(newPath); + rc = "false"; + } catch (DataGridException e) { + logger.debug("Path {} does not exist. Executing modification", newPath, e); + } + return rc; + } + + /* + * ************************************************************************* + * ******************************** UTILS ********************************** + * ************************************************************************* + */ + + /** + * Finds all collections and data objects existing under a certain path + * + * @param request + * contains all parameters in a map, we can use it to get all + * parameters passed in request + * @return json with collections and data objects + * @throws DataGridConnectionRefusedException + */ + @RequestMapping(value = "getPaginatedJSONObjs/") + @ResponseBody + public String getPaginatedJSONObjs(final HttpServletRequest request) throws DataGridConnectionRefusedException { + List dataGridCollectionAndDataObjects; + + int draw = Integer.parseInt(request.getParameter("draw")); + int start = Integer.parseInt(request.getParameter("start")); + int length = Integer.parseInt(request.getParameter("length")); + String searchString = request.getParameter("search[value]"); + int orderColumn = Integer.parseInt(request.getParameter("order[0][column]")); + String orderDir = request.getParameter("order[0][dir]"); + boolean deployRule = request.getParameter("rulesdeployment") != null; + + // Pagination context to get the sequence number for the listed items + DataGridPageContext pageContext = new DataGridPageContext(); + + ObjectMapper mapper = new ObjectMapper(); + Map jsonResponse = new HashMap(); + jsonResponse.put("draw", String.valueOf(draw)); + jsonResponse.put("recordsTotal", String.valueOf(1)); + jsonResponse.put("recordsFiltered", String.valueOf(0)); + jsonResponse.put("data", new ArrayList()); + String jsonString = ""; + + try { + String path = currentPath; + if (deployRule) { + path = ruleDeploymentService.getRuleCachePath(); + } + + Double startPage = Math.floor(start / length) + 1; + dataGridCollectionAndDataObjects = cs.getSubCollectionsAndDataObjectsUnderPathThatMatchSearchTextPaginated( + path, searchString, startPage.intValue(), length, orderColumn, orderDir, pageContext); + totalObjsForCurrentSearch = pageContext.getTotalNumberOfItems(); + totalObjsForCurrentPath = pageContext.getTotalNumberOfItems(); + + jsonResponse.put("recordsTotal", String.valueOf(totalObjsForCurrentPath)); + jsonResponse.put("recordsFiltered", String.valueOf(totalObjsForCurrentSearch)); + jsonResponse.put("data", dataGridCollectionAndDataObjects); + } catch (DataGridConnectionRefusedException e) { + throw e; + } catch (Exception e) { + logger.error("Could not get collections/data objs under path {}: {}", currentPath, e.getMessage()); + } + + try { + jsonString = mapper.writeValueAsString(jsonResponse); + } catch (JsonProcessingException e) { + logger.error("Could not parse hashmap in collections to json: {}", e.getMessage()); + } + + return jsonString; + } + + /** + * @return the sourcePaths + */ + public List getSourcePaths() { + return sourcePaths; + } + + /** + * @return the currentPath + */ + public String getCurrentPath() { + return currentPath; + } + + public String getParentPath() { + return parentPath; + } + + /** + * Removes a path from the user's navigation history + * + * @param path + * path to be removed + */ + public void removePathFromHistory(final String path) { + if (path == null || path.isEmpty()) { + return; + } + + collectionHistoryBack.remove(path); + collectionHistoryForward.remove(path); + } + + /* + * ************************************************************************** + * **************************** PRIVATE METHODS ***************************** + * ************************************************************************** + */ + + /** + * Sets the current path and parent path based on a given path. + * + * @param path + * new path to update current path and parent path + */ + private void assignNewValuesToCurrentAndParentPath(final String path) { + if (path == null || path.isEmpty()) { + return; + } + + currentPath = path; + parentPath = currentPath.substring(0, currentPath.lastIndexOf("/") + 1); + } + + /** + * Creates the breadcrumb based on a given path. + * + * @param model + * Model attribute to set variables to be used in the view + * @param path + * path that will be displayed in the breadcrumb + */ + private void setBreadcrumbToModel(final Model model, final String path) { + DataGridCollectionAndDataObject obj; + try { + obj = cs.findByName(path); + } catch (DataGridException e) { + obj = new DataGridCollectionAndDataObject(); + obj.setPath(path); + obj.setCollection(false); + obj.setParentPath(path.substring(0, path.lastIndexOf("/") + 1)); + obj.setName(path.substring(path.lastIndexOf("/") + 1, path.length())); + logger.error("Could not find DataGridCollectionAndDataObject by path: {}", e.getMessage()); + } + + setBreadcrumbToModel(model, obj); + } + + /** + * Creates the breadcrumb based on a given path. + * + * @param model + * Model attribute to set variables to be used in the view + * @param obj + * {@code DataGridCollectionAndDataObject} object + */ + private void setBreadcrumbToModel(final Model model, final DataGridCollectionAndDataObject obj) { + ArrayList listHistoryBack = new ArrayList(collectionHistoryBack); + Collections.reverse(listHistoryBack); + + DataGridUser user = loggedUserUtils.getLoggedDataGridUser(); + boolean isPathFavorite = favoritesService.isPathFavoriteForUser(user, obj.getPath()); + + model.addAttribute("starredPath", isPathFavorite); + model.addAttribute("collectionPastHistory", listHistoryBack); + model.addAttribute("collectionPastHistoryEmpty", collectionHistoryBack.isEmpty()); + model.addAttribute("collectionForwardHistory", collectionHistoryForward); + model.addAttribute("collectionForwardHistoryEmpty", collectionHistoryForward.isEmpty()); + model.addAttribute("collectionForwardHistory", collectionHistoryForward); + model.addAttribute("collectionAndDataObject", obj); + model.addAttribute("breadcrumb", new DataGridBreadcrumb(obj.getPath())); + model.addAttribute("homeCollectionName", irodsServices.getCurrentUser()); + } + + /** + * Finds all collections and data objects existing under a certain path + * + * @param model + * @param path + * path to get all directories and data objects from + * @return collections browser template that renders all items of certain path + * (parent) + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the grid. + */ + private String getCollBrowserView(final Model model, String path) throws DataGridException { + logger.info("getCollBrowserView()"); + if (cs.isPathValid(path)) { + if (path.endsWith("/") && path.compareTo("/") != 0) { + path = path.substring(0, path.length() - 1); + } + currentPath = path; + } else { + model.addAttribute("invalidPath", path); + path = currentPath; + } + + DataGridUser user = loggedUserUtils.getLoggedDataGridUser(); + logger.info("find collection by name:{}", path); + DataGridCollectionAndDataObject dataGridObj = cs.findByName(path); + + if (dataGridObj.isDataObject()) { + dataGridObj.setChecksum(cs.getChecksum(path)); + dataGridObj.setNumberOfReplicas(cs.getTotalNumberOfReplsForDataObject(path)); + dataGridObj.setReplicaNumber(String.valueOf(cs.getReplicationNumber(path))); + } + + permissionsService.resolveMostPermissiveAccessForUser(dataGridObj, user); + + if (zoneTrashPath == null || zoneTrashPath.isEmpty()) { + zoneTrashPath = String.format("/%s/trash", irodsServices.getCurrentUserZone()); + } + + CollectionOrDataObjectForm collectionForm = new CollectionOrDataObjectForm(); + collectionForm.setInheritOption(cs.getInheritanceOptionForCollection(currentPath)); + + String permissionType = cs.getPermissionsForPath(path); + boolean isPermissionOwn = "own".equals(permissionType); + boolean isTrash = path.contains(zoneTrashPath) && (isPermissionOwn || user.isAdmin()); + boolean inheritanceDisabled = !isPermissionOwn && collectionForm.getInheritOption(); + + model.addAttribute("collectionAndDataObject", dataGridObj); + model.addAttribute("isTrash", isTrash); + model.addAttribute("permissionType", permissionType); + model.addAttribute("currentPath", currentPath); + model.addAttribute("isCurrentPathCollection", cs.isCollection(path)); + model.addAttribute("user", user); + model.addAttribute("trashColl", cs.getTrashForPath(currentPath)); + model.addAttribute("collection", collectionForm); + model.addAttribute("inheritanceDisabled", inheritanceDisabled); + model.addAttribute("requestMapping", "/collections/add/action/"); + model.addAttribute("parentPath", parentPath); + + setBreadcrumbToModel(model, dataGridObj); + + return "collections/collectionsBrowser"; + } + + /** + * Adds a given path to the list of paths visited by the user + * + * @param path + * path to a collection or data object to be added to history + */ + private void addPathToHistory(final String path) { + if (path.equals(currentPath)) { + return; + } + + while (collectionHistoryBack.size() >= MAX_HISTORY_SIZE) { + collectionHistoryBack.remove(0); + } + + collectionHistoryBack.push(currentPath); + + if (!collectionHistoryForward.isEmpty()) { + collectionHistoryForward.clear(); + } + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/FavoritesController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/FavoritesController.java new file mode 100755 index 000000000..07174a906 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/FavoritesController.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.WebApplicationContext; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserFavorite; +import com.emc.metalnx.services.interfaces.FavoritesService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.UserService; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@RequestMapping(value = "/favorites") +public class FavoritesController { + @Autowired + FavoritesService favoritesService; + + @Autowired + UserService userService; + + @Autowired + IRODSServices irodsServices; + + private static final String REQUEST_OK = "OK"; + private static final String REQUEST_ERROR = "ERROR"; + + private int totalFavorites; + private int totalFavoritesFiltered; + + private static final Logger logger = LoggerFactory.getLogger(FavoritesController.class); + + /** + * Responds to the list favorites request + * + * @param model + * @return the template with a list of favorite items + */ + @RequestMapping(value = "/") + public String listfavorites(final Model model) { + String loggedUsername = irodsServices.getCurrentUser(); + String loggedUserZoneName = irodsServices.getCurrentUserZone(); + DataGridUser user = userService.findByUsernameAndAdditionalInfo(loggedUsername, loggedUserZoneName); + + List userFavorites = user.getFavoritesSorted(); + + model.addAttribute("userFavorites", userFavorites); + + return "favorites/favorites"; + } + + /** + * Add a path to the favorites list + * + * @param path + * path to be added to the favorites + */ + @RequestMapping(value = "/addFavoriteToUser/") + @ResponseBody + public String addFavoriteToUser(@RequestParam("path") final String path) { + String zoneName = irodsServices.getCurrentUserZone(); + String username = irodsServices.getCurrentUser(); + + logger.info("Request for adding a {} favorite from {}", path, username); + DataGridUser user = userService.findByUsernameAndAdditionalInfo(username, zoneName); + + Set toAdd = new HashSet(); + toAdd.add(path); + + boolean operationResult = favoritesService.updateFavorites(user, toAdd, null); + + return operationResult ? REQUEST_OK : REQUEST_ERROR; + } + + /** + * Remove a path to the favorites list + * + * @param model + * @param path + * path to be removed from the favorites + */ + @RequestMapping(value = "/removeFavoriteFromUser/") + @ResponseBody + public String removeFavoriteFromUser(@RequestParam("path") final String path) { + String username = irodsServices.getCurrentUser(); + logger.info("Request for removing a {} favorite from {}", path, username); + + String zoneName = irodsServices.getCurrentUserZone(); + DataGridUser user = userService.findByUsernameAndAdditionalInfo(username, zoneName); + + Set toRemove = new HashSet(); + toRemove.add(path); + + boolean operationResult = favoritesService.updateFavorites(user, null, toRemove); + + return operationResult ? REQUEST_OK : REQUEST_ERROR; + } + + @RequestMapping(value = "/favoritesPaginated") + @ResponseBody + public String favoritesPaginated(final HttpServletRequest request) { + + int draw = Integer.parseInt(request.getParameter("draw")); + int start = Integer.parseInt(request.getParameter("start")); + int length = Integer.parseInt(request.getParameter("length")); + String searchString = request.getParameter("search[value]"); + int orderColumn = Integer.parseInt(request.getParameter("order[0][column]")); + String orderDir = request.getParameter("order[0][dir]"); + boolean onlyCollections = Boolean.parseBoolean(request.getParameter("onlyCollections")); + String loggedUsername = irodsServices.getCurrentUser(); + String loggedUserZoneName = irodsServices.getCurrentUserZone(); + DataGridUser user = userService.findByUsernameAndAdditionalInfo(loggedUsername, loggedUserZoneName); + String[] orderBy = { "name", "path", "created_at", "is_collection" }; + + List userFavorites = favoritesService.findFavoritesPaginated(user, start, length, + searchString, orderBy[orderColumn], orderDir, onlyCollections); + + ObjectMapper mapper = new ObjectMapper(); + Map jsonResponse = new HashMap(); + String jsonString = ""; + if ("".equals(searchString)) { + totalFavorites = user.getFavorites().size(); + totalFavoritesFiltered = user.getFavorites().size(); + } else { + totalFavoritesFiltered = userFavorites.size(); + } + + jsonResponse.put("draw", String.valueOf(draw)); + jsonResponse.put("recordsTotal", String.valueOf(totalFavorites)); + jsonResponse.put("recordsFiltered", String.valueOf(totalFavoritesFiltered)); + jsonResponse.put("data", userFavorites); + + try { + jsonString = mapper.writeValueAsString(jsonResponse); + } catch (Exception e) { + logger.error("Could not parse hashmap in favorites to json", e.getMessage()); + } + return jsonString; + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/FileOperationsController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/FileOperationsController.java new file mode 100755 index 000000000..4afcff051 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/FileOperationsController.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.irods.jargon.core.exception.JargonException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.WebApplicationContext; + +import com.emc.metalnx.controller.utils.LoggedUserUtils; +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.enums.DataGridPermType; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridReplicateException; +import com.emc.metalnx.modelattribute.collection.CollectionOrDataObjectForm; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FileOperationService; +import com.emc.metalnx.services.interfaces.IRODSServices; + +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@RequestMapping(value = "/fileOperation") +public class FileOperationsController { + + private static final Logger logger = LoggerFactory.getLogger(FileOperationsController.class); + private static String TRASH_PATH; + + @Autowired + private CollectionController collectionController; + + @Autowired + private CollectionService collectionService; + + @Autowired + private IRODSServices irodsServices; + + @Autowired + private FileOperationService fileOperationService; + + @Autowired + private LoggedUserUtils loggedUserUtils; + + // contains the path to the file that will be downloaded + private String filePathToDownload; + + // checks if it's necessary to remove any temporary collections created for + // downloading + private boolean removeTempCollection; + + @PostConstruct + public void init() { + TRASH_PATH = String.format("/%s/trash/home/%s", irodsServices.getCurrentUserZone(), + irodsServices.getCurrentUser()); + } + + @RequestMapping(value = "/move", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + public String move(final Model model, @RequestParam("targetPath") final String targetPath, + @RequestParam("paths[]") final String[] paths) throws DataGridException, JargonException { + + List failedMoves = new ArrayList<>(); + String fileMoved = ""; + + try { + for (String p : paths) { + String item = p.substring(p.lastIndexOf("/") + 1, p.length()); + if (!fileOperationService.move(p, targetPath)) { + failedMoves.add(item); + } else if (paths.length == 1) { + fileMoved = item; + } + } + } catch (DataGridException e) { + logger.error("Could not move item to {}: {}", targetPath, e.getMessage()); + } + + if (!fileMoved.isEmpty()) { + model.addAttribute("fileMoved", fileMoved); + } + + model.addAttribute("failedMoves", failedMoves); + + return collectionController.getSubDirectories(model, targetPath); + } + + @RequestMapping(value = "/copy", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + public String copy(final Model model, @RequestParam("targetPath") final String targetPath, + @RequestParam("copyWithMetadata") final boolean copyWithMetadata, + @RequestParam("paths[]") final String[] paths) throws DataGridException, JargonException { + + List failedCopies = new ArrayList<>(); + String fileCopied = ""; + + for (String p : paths) { + String item = p.substring(p.lastIndexOf("/") + 1, p.length()); + if (!fileOperationService.copy(p, targetPath, copyWithMetadata)) { + failedCopies.add(item); + } else if (paths.length == 1) { + fileCopied = item; + } + } + + if (!fileCopied.isEmpty()) { + model.addAttribute("fileCopied", fileCopied); + } + + model.addAttribute("failedCopies", failedCopies); + + return collectionController.getSubDirectories(model, targetPath); + } + + /** + * Delete a replica of a data object + * + * @param model + * MVC model + * @param path + * path to the parent of the data object to be deleted + * @param fileName + * name of the data object to be deleted + * @param replicaNumber + * number of the replica that is going to be deleted + * @return the template that shows the data object information with the replica + * table refreshed + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the data grid + */ + @RequestMapping(value = "deleteReplica", method = RequestMethod.POST) + public String deleteReplica(final Model model, @RequestParam("path") final String path, + @RequestParam("fileName") final String fileName, @RequestParam final String replicaNumber) + throws DataGridConnectionRefusedException { + + boolean inAdminMode = loggedUserUtils.getLoggedDataGridUser().isAdmin(); + + if (fileOperationService.deleteReplica(path, fileName, Integer.parseInt(replicaNumber), inAdminMode)) { + model.addAttribute("delReplReturn", "success"); + } else { + model.addAttribute("delReplReturn", "failure"); + } + return collectionController.getFileInfo(model, path); + } + + @RequestMapping(value = "/replicate", method = RequestMethod.POST) + public String replicate(final Model model, final HttpServletRequest request) + throws DataGridConnectionRefusedException { + + List sourcePaths = collectionController.getSourcePaths(); + String[] resources = request.getParameterMap().get("resourcesForReplication"); + List failedReplicas = new ArrayList<>(); + + if (resources != null) { + String targetResource = resources[0]; + boolean inAdminMode = loggedUserUtils.getLoggedDataGridUser().isAdmin(); + for (String sourcePathItem : sourcePaths) { + try { + fileOperationService.replicateDataObject(sourcePathItem, targetResource, inAdminMode); + } catch (DataGridReplicateException e) { + String item = sourcePathItem.substring(sourcePathItem.lastIndexOf("/") + 1, + sourcePathItem.length()); + failedReplicas.add(item); + } + } + } + + if (!sourcePaths.isEmpty()) { + model.addAttribute("failedReplicas", failedReplicas); + sourcePaths.clear(); + } + + return collectionController.index(model, request, false); + } + + @RequestMapping(value = "/prepareFilesForDownload/", method = RequestMethod.GET) + public void prepareFilesForDownload(final HttpServletResponse response, + @RequestParam("paths[]") final String[] paths) throws DataGridConnectionRefusedException { + + try { + if (paths.length > 1 || !collectionService.isDataObject(paths[0])) { + filePathToDownload = collectionService.prepareFilesForDownload(paths); + removeTempCollection = true; + } else { + // if a single file was selected, it will be transferred directly through the + // HTTP response + removeTempCollection = false; + filePathToDownload = paths[0]; + String permissionType = collectionService.getPermissionsForPath(filePathToDownload); + if (permissionType.equalsIgnoreCase(DataGridPermType.NONE.name())) { + throw new DataGridException("Lack of permission to download file " + filePathToDownload); + } + } + } catch (DataGridConnectionRefusedException e) { + throw e; + } catch (DataGridException | IOException e) { + logger.error("Could not download selected items: ", e.getMessage()); + response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + } + } + + @RequestMapping(value = "/download/", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public void download(final HttpServletResponse response) throws DataGridConnectionRefusedException { + + try { + fileOperationService.download(filePathToDownload, response, removeTempCollection); + filePathToDownload = ""; + removeTempCollection = false; + } catch (DataGridException | IOException e) { + logger.error("Could not download selected items: ", e.getMessage()); + } + } + + @RequestMapping(value = "/delete/", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + public String deleteCollectionAndDataObject(final Model model, @RequestParam("paths[]") final String[] paths) + throws DataGridException, JargonException { + boolean forceRemove; + List failedDeletions = new ArrayList<>(); + String fileDeleted = null; + + for (String path : paths) { + forceRemove = path.startsWith(TRASH_PATH); + + if (!fileOperationService.deleteItem(path, forceRemove)) { + String item = path.substring(path.lastIndexOf("/") + 1, path.length()); + failedDeletions.add(item); + } else if (paths.length == 1) { + fileDeleted = path.substring(path.lastIndexOf("/") + 1, path.length()); + } + + collectionController.removePathFromHistory(path); + } + + if (fileDeleted != null) { + model.addAttribute("fileDeleted", fileDeleted); + } + + model.addAttribute("failedDeletions", failedDeletions); + model.addAttribute("currentPath", collectionController.getCurrentPath()); + model.addAttribute("parentPath", collectionController.getParentPath()); + + return collectionController.getSubDirectories(model, collectionController.getCurrentPath()); + } + + @RequestMapping(value = "emptyTrash/", method = RequestMethod.POST) + public ResponseEntity emptyTrash() throws DataGridConnectionRefusedException { + String trashForCurrentPath = collectionService.getTrashForPath(collectionController.getCurrentPath()); + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + if (fileOperationService.emptyTrash(loggedUser, trashForCurrentPath)) { + return new ResponseEntity<>(HttpStatus.OK); + } + + return new ResponseEntity<>(HttpStatus.METHOD_NOT_ALLOWED); + } + + /** + * Displays the modify user form with all fields set to the selected collection + * + * @param model + * MVC model + * @return collectionForm with fields set + * @throws DataGridException + * if item cannot be modified + */ + @RequestMapping(value = "modify/", method = RequestMethod.GET) + public String showModifyForm(final Model model, @RequestParam("path") final String path) throws DataGridException { + String currentPath = collectionController.getCurrentPath(); + String parentPath = collectionController.getParentPath(); + + String formType = "editDataObjectForm"; + CollectionOrDataObjectForm targetForm = new CollectionOrDataObjectForm(); + DataGridCollectionAndDataObject dataGridCollectionAndDataObject = collectionService.findByName(path); + + logger.info("Modify form for {}", path); + + targetForm.setCollectionName(dataGridCollectionAndDataObject.getName()); + targetForm.setPath(dataGridCollectionAndDataObject.getPath()); + targetForm.setParentPath(currentPath); + + if (dataGridCollectionAndDataObject.isCollection()) { + formType = "editCollectionForm"; + targetForm.setCollection(true); + logger.info("Setting inheritance for {}", path); + targetForm.setInheritOption(collectionService.getInheritanceOptionForCollection(targetForm.getPath())); + } + + model.addAttribute("currentPath", currentPath); + model.addAttribute("parentPath", parentPath); + model.addAttribute("collection", targetForm); + model.addAttribute("requestMapping", "/collections/modify/action/"); + + return String.format("collections/%s", formType); + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/FilePropertiesController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/FilePropertiesController.java new file mode 100755 index 000000000..965609885 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/FilePropertiesController.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.irods.jargon.core.exception.JargonException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.emc.metalnx.controller.utils.LoggedUserUtils; +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridFilePropertySearch; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.enums.DataGridSearchOperatorEnum; +import com.emc.metalnx.core.domain.entity.enums.FilePropertyField; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.FilePropertyService; +import com.emc.metalnx.services.interfaces.UserService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Controller +@RequestMapping(value = "/fileproperty") +public class FilePropertiesController { + + @Autowired + FilePropertyService filePropertyService; + + @Autowired + CollectionService collectionService; + + @Autowired + UserService userService; + + @Autowired + LoggedUserUtils loggedUserUtils; + + @Value("${irods.zoneName}") + private String zoneName; + + // ui mode that will be shown when the rods user switches mode from admin to + // user and vice-versa + public static final String UI_USER_MODE = "user"; + public static final String UI_ADMIN_MODE = "admin"; + + // current page the user currently is (pagination) + private int currentPage = 1; + + // current metadata search + List currentFilePropertySearch; + + private String jsonFilePropertySearch; + + private static final Logger logger = LoggerFactory.getLogger(FilePropertiesController.class); + + @RequestMapping(value = "/") + public String index(final Model model, final HttpServletRequest request, + @RequestParam(value = "backFromCollections", required = false) final boolean backFromCollections) { + + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + String uiMode = (String) request.getSession().getAttribute("uiMode"); + if (uiMode == null || uiMode.isEmpty()) { + if (loggedUser.isAdmin()) { + uiMode = UI_ADMIN_MODE; + } else { + uiMode = UI_USER_MODE; + } + } + + if (backFromCollections) { + model.addAttribute("jsonFilePropertySearch", jsonFilePropertySearch); + } + model.addAttribute("uiMode", uiMode); + return "metadata/metadataDisplay"; + } + + @RequestMapping(value = "/search") + @ResponseBody + public String search( + @RequestParam(value = "jsonFilePropertySearch", required = false) final String jsonFilePropertySearch, + @RequestParam("draw") final int draw, @RequestParam("start") final int start, + @RequestParam("length") final int length) throws DataGridConnectionRefusedException, JargonException { + + if (jsonFilePropertySearch != null) { + currentPage = (int) (Math.floor(start / length) + 1); + this.jsonFilePropertySearch = jsonFilePropertySearch; + } + + ObjectMapper mapper = new ObjectMapper(); + Map jsonResponse = new HashMap(); + jsonResponse.put("draw", String.valueOf(draw)); + jsonResponse.put("recordsTotal", String.valueOf(0)); + jsonResponse.put("recordsFiltered", String.valueOf(0)); + jsonResponse.put("data", new ArrayList()); + String jsonString = ""; + + try { + JsonNode jsonNode = mapper.readTree(this.jsonFilePropertySearch); + + currentFilePropertySearch = new ArrayList<>(); + + JsonNode attributes = jsonNode.get("attribute"); + JsonNode operators = jsonNode.get("operator"); + JsonNode values = jsonNode.get("value"); + for (int i = 0; i < attributes.size(); i++) { + DataGridFilePropertySearch ms = new DataGridFilePropertySearch( + FilePropertyField.valueOf(attributes.get(i).textValue()), + DataGridSearchOperatorEnum.valueOf(operators.get(i).textValue()), values.get(i).textValue()); + currentFilePropertySearch.add(ms); + } + + DataGridPageContext pageContext = new DataGridPageContext(); + + List dataGridCollectionAndDataObjects = filePropertyService + .findByFileProperties(currentFilePropertySearch, pageContext, currentPage, length); + + jsonResponse.put("recordsTotal", String.valueOf(pageContext.getTotalNumberOfItems())); + jsonResponse.put("recordsFiltered", String.valueOf(pageContext.getTotalNumberOfItems())); + jsonResponse.put("data", dataGridCollectionAndDataObjects); + + } catch (DataGridConnectionRefusedException e) { + logger.error("data grid error in search", e); + throw e; + } catch (JargonException e) { + logger.error("Could not search by metadata: ", e.getMessage()); + throw e; + } catch (ParseException e) { + logger.error("Could not search by metadata: ", e.getMessage()); + throw new JargonException(e); + } catch (JsonProcessingException e) { + logger.error("Could not search by metadata: ", e.getMessage()); + throw new JargonException(e); + } catch (IOException e) { + logger.error("Could not search by metadata: ", e.getMessage()); + throw new JargonException(e); + } + + try { + jsonString = mapper.writeValueAsString(jsonResponse); + } catch (JsonProcessingException e) { + logger.error("Could not parse hashmap in file properties search to json: {}", e.getMessage()); + throw new JargonException(e); + + } + + return jsonString; + } + + @RequestMapping(value = "/downloadCSVResults/") + public void searchToCSVFile(final HttpServletResponse response) + throws DataGridConnectionRefusedException, IOException, JargonException { + + ServletOutputStream outputStream = response.getOutputStream(); + String loggedUser = getLoggedDataGridUser().getUsername(); + String date = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + + String filename = String.format("search-result_%s_%s.csv", loggedUser, date); + + // Setting CSV Mime type + response.setContentType("text/csv"); + response.setHeader("Content-disposition", "attachment;filename=" + filename); + + // Building search parameters lines + outputStream.print("Search condition #;Condition\n"); + int i = 1; + for (DataGridFilePropertySearch field : currentFilePropertySearch) { + String condition = field.toString(); + outputStream.print(String.format("%d;%s\n", i, condition)); + i++; + } + outputStream.print("\n"); + outputStream.flush(); + + // Executing query + DataGridPageContext pageContext = new DataGridPageContext(); + List dataGridCollectionAndDataObjects = filePropertyService + .findByFileProperties(currentFilePropertySearch, pageContext, 1, Integer.MAX_VALUE); + + // Printing number of results + outputStream.print("Number of results\n"); + outputStream.print(String.format("%d\n", pageContext.getTotalNumberOfItems())); + outputStream.print("\n"); + outputStream.flush(); + + // Printing results + outputStream.print("Filename;Path;Owner;Kind;Modified;Size;Matches\n"); + for (DataGridCollectionAndDataObject obj : dataGridCollectionAndDataObjects) { + outputStream.print(obj.getName() + ";"); + outputStream.print(obj.getPath() + ";"); + outputStream.print(obj.getOwner() + ";"); + outputStream.print((obj.isCollection() ? "collection" : "data object") + ";"); + outputStream.print(obj.getModifiedAtFormattedForCSVReport() + ";"); + outputStream.print(String.valueOf(obj.getSize()) + ";"); + outputStream.print(String.valueOf(obj.getNumberOfMatches())); + outputStream.print("\n"); + outputStream.flush(); + } + } + + private DataGridUser getLoggedDataGridUser() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + String username = (String) auth.getPrincipal(); + + return userService.findByUsernameAndAdditionalInfo(username, zoneName); + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/GroupBookmarkController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/GroupBookmarkController.java new file mode 100755 index 000000000..b19af6c0f --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/GroupBookmarkController.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.irods.jargon.core.exception.JargonException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.SessionAttributes; + +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridGroupBookmark; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.AdminServices; +import com.emc.metalnx.services.interfaces.GroupBookmarkService; +import com.emc.metalnx.services.interfaces.GroupService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Controller +@SessionAttributes({ "addBookmarks", "removeBookmarks" }) +@RequestMapping(value = "/groupBookmarks") +public class GroupBookmarkController { + @Autowired + GroupBookmarkService groupBookmarkService; + + @Autowired + GroupService groupService; + + @Autowired + IRODSServices irodsServices; + + @Autowired + AdminServices adminServices; + + private Set addBookmark = new HashSet(); + private Set removeBookmark = new HashSet(); + + private static final String REQUEST_OK = "OK"; + private static final String REQUEST_ERROR = "ERROR"; + + private int totalGroupBookmarks = 0; + private int totalGroupBookmarksFiltered = 0; + + public final static Logger logger = LoggerFactory.getLogger(GroupBookmarkController.class); + + /** + * List users by page number + * + * @param model + * @return + */ + @RequestMapping(value = "/groups/") + public String listGroupsByQuery(final Model model) throws DataGridConnectionRefusedException { + String currentUser = ""; + String currentZone = ""; + List groups = null; + try { + currentUser = irodsServices.getCurrentUser(); + currentZone = irodsServices.getCurrentUserZone(); + groups = groupBookmarkService.getGroupsBookmarks(currentUser, currentZone); + } catch (JargonException e) { + logger.error("Could not get groups bookmarks for {}", currentUser, e); + groups = new ArrayList(); + } + + model.addAttribute("groups", groups); + + return "groups/groupsCollections"; + } + + @RequestMapping(value = "/addBookmark/") + @ResponseStatus(value = HttpStatus.OK) + public void addBookmark(@RequestParam("path") final String path, final Model model) { + + // Remove from the in-memory list before checking database + if (removeBookmark.contains(path)) { + removeBookmark.remove(path); + } else { + addBookmark.add(path); + model.addAttribute("addBookmark", addBookmark); + } + } + + @RequestMapping(value = "/removeBookmark/") + @ResponseStatus(value = HttpStatus.OK) + public void removeBookmark(@RequestParam("path") final String path) { + + // Removing from the in-memory bookmarks list before checking database + if (addBookmark.contains(path)) { + addBookmark.remove(path); + } else { + removeBookmark.add(path); + } + } + + @RequestMapping(value = "/addBookmarkToGroup/") + @ResponseBody + public String addBookmarkToGroup(@RequestParam("groupName") final String groupName, + @RequestParam("path") final String path) { + + String zoneName = irodsServices.getCurrentUserZone(); + DataGridGroup group = groupService.findByGroupnameAndZone(groupName, zoneName); + + if (group == null) { + return REQUEST_ERROR; + } + + Set toAdd = new HashSet(); + toAdd.add(path); + + groupBookmarkService.updateBookmarks(group, toAdd, null); + + return REQUEST_OK; + } + + @RequestMapping(value = "/removeBookmarkFromGroup/") + @ResponseBody + public String removeBookmarkFromGroup(@RequestParam("groupName") final String groupName, + @RequestParam("path") final String path) { + + String zoneName = irodsServices.getCurrentUserZone(); + DataGridGroup group = groupService.findByGroupnameAndZone(groupName, zoneName); + + if (group == null) { + return REQUEST_ERROR; + } + + Set toRemove = new HashSet(); + toRemove.add(path); + + boolean operationResult = groupBookmarkService.updateBookmarks(group, null, toRemove); + + return operationResult ? REQUEST_OK : REQUEST_ERROR; + } + + @RequestMapping(value = "/groupsBookmarksPaginated") + @ResponseBody + public String listGroupsByQueryPaginatedModel(final HttpServletRequest request) + throws DataGridConnectionRefusedException { + + int draw = Integer.parseInt(request.getParameter("draw")); + int start = Integer.parseInt(request.getParameter("start")); + int length = Integer.parseInt(request.getParameter("length")); + String searchString = request.getParameter("search[value]"); + int orderColumn = Integer.parseInt(request.getParameter("order[0][column]")); + String orderDir = request.getParameter("order[0][dir]"); + boolean onlyCollections = Boolean.parseBoolean(request.getParameter("onlyCollections")); + String currentUser = irodsServices.getCurrentUser(); + String currentZone = irodsServices.getCurrentUserZone(); + String[] orderBy = { "Dggb.name", "Dggb.path", "Dgg.groupname", "Dggb.createTs" }; + List groupBookmarks = null; + + ObjectMapper mapper = new ObjectMapper(); + Map jsonResponse = new HashMap(); + String jsonString = ""; + + try { + groupBookmarks = groupBookmarkService.getGroupsBookmarksPaginated(currentUser, currentZone, start, length, + searchString, orderBy[orderColumn], orderDir, onlyCollections); + + if ("".equals(searchString)) { + totalGroupBookmarks = groupBookmarkService.countTotalGroupBookmarks(currentUser, currentZone); + totalGroupBookmarksFiltered = totalGroupBookmarks; + } else { + totalGroupBookmarksFiltered = groupBookmarks.size(); + } + } catch (JargonException e) { + logger.error("Could not get groups bookmarks for {}", currentUser); + groupBookmarks = new ArrayList(); + } + + jsonResponse.put("draw", String.valueOf(draw)); + jsonResponse.put("recordsTotal", String.valueOf(totalGroupBookmarks)); + jsonResponse.put("recordsFiltered", String.valueOf(totalGroupBookmarksFiltered)); + jsonResponse.put("data", groupBookmarks); + + try { + jsonString = mapper.writeValueAsString(jsonResponse); + } catch (Exception e) { + logger.error("Could not parse hashmap in favorites to json", e.getMessage()); + } + return jsonString; + } + + public void clearBookmarksLists() { + addBookmark.clear(); + removeBookmark.clear(); + } + + /** + * @return the addBookmark + */ + public Set getAddBookmark() { + return addBookmark; + } + + /** + * @return the removeBookmark + */ + public Set getRemoveBookmark() { + return removeBookmark; + } + + /** + * Add a given path to the list of bookmarks + * + * @param path + * path to any collection or file in the grid to be bookmarked + */ + public boolean addPathAsGroupBookmark(final String path) { + if (path == null || path.isEmpty()) { + return false; + } + + return addBookmark.add(path); + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/HttpErrorController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/HttpErrorController.java new file mode 100755 index 000000000..2408baaa6 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/HttpErrorController.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.WebApplicationContext; + +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@RequestMapping(value = "/httpError") +public class HttpErrorController { + + private static final Logger logger = LoggerFactory.getLogger(HttpErrorController.class); + + /** + * Responds the error 404 + * + * @param model + * @return the error 404 custom page + */ + @RequestMapping(value = "/404") + public String show404CustomizedPage(final Model model) { + logger.error("404 - Page not found"); + + return "httpErrors/404"; + } + + /** + * Responds the error 403 + * + * @param model + * @return the error 403 custom page + */ + @RequestMapping(value = "/403") + public String show403CustomizedPage(final Model model) { + logger.error("403 - Access denied"); + + return "httpErrors/403"; + } + + /** + * Responds the error 500 + * + * @param model + * @return the error 500 custom page + */ + @RequestMapping(value = "/500") + public String show500CustomizedPage(final Model model, HttpServletRequest httpRequest) { + logger.error("500 - Internal Server Error"); + logger.info("httpRequest:{}", httpRequest); + + return "httpErrors/500"; + } + + /** + * Responds the server not respoding + * + * @param model + * @return the server not responding error custom page + */ + @RequestMapping(value = "/serverNotResponding") + public String showServerNotRespondingCustomizedPage(final Model model) { + logger.error("503 - Connect Exception (iCAT Server not responding)"); + + return "httpErrors/serverNotResponding"; + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/LoginController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/LoginController.java new file mode 100755 index 000000000..b263cb09a --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/LoginController.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.servlet.ModelAndView; + +import com.emc.metalnx.services.auth.UserTokenDetails; + +@Controller +@RequestMapping(value = "/login") +public class LoginController { + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String loginView(final Model model) { + + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + if (auth instanceof UsernamePasswordAuthenticationToken) { + boolean isUserAdmin = ((UserTokenDetails) auth.getDetails()).getUser().isAdmin(); + return isUserAdmin ? "redirect:/dashboard/" : "redirect:/collections/"; + } + + return "login/index"; + } + + @RequestMapping(value = "/exception", method = RequestMethod.GET) + public ModelAndView loginErrorHandler(final Exception e) { + + ModelAndView model = new ModelAndView("login/index"); + model.addObject("usernameOrPasswordInvalid", true); + + return model; + } + + @RequestMapping(value = "/invalidSession/", method = RequestMethod.GET) + @ResponseStatus(HttpStatus.REQUEST_TIMEOUT) + @ResponseBody + public String invalidSessionHandler(final HttpServletRequest response) { + return ""; + } + + @RequestMapping(value = "/serverNotResponding", method = RequestMethod.GET) + public ModelAndView loginServerNotRespondingErrorHandler(final Exception e) { + + ModelAndView model = new ModelAndView("login/index"); + model.addObject("serverNotResponding", true); + + return model; + } + + @RequestMapping(value = "/databaseNotResponding", method = RequestMethod.GET) + public ModelAndView databaseNotRespondingErrorHandler(final Exception e) { + + ModelAndView model = new ModelAndView("login/index"); + model.addObject("databaseNotResponding", true); + + return model; + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/MetadataController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/MetadataController.java new file mode 100755 index 000000000..e1a3c52ae --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/MetadataController.java @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.context.WebApplicationContext; + +import com.emc.metalnx.controller.utils.LoggedUserUtils; +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridMetadata; +import com.emc.metalnx.core.domain.entity.DataGridMetadataSearch; +import com.emc.metalnx.core.domain.entity.DataGridPageContext; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.enums.DataGridSearchOperatorEnum; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.MetadataService; +import com.emc.metalnx.services.interfaces.PermissionsService; +import com.emc.metalnx.services.interfaces.UserService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@SessionAttributes({ "sourcePaths" }) +@RequestMapping(value = "/metadata") +public class MetadataController { + + @Autowired + MetadataService metadataService; + + @Autowired + CollectionService collectionService; + + @Autowired + UserService userService; + + @Autowired + PermissionsService permissionsService; + + @Autowired + LoggedUserUtils loggedUserUtils; + + @Autowired + CollectionController collectionController; + + private String jsonMetadataSearch; + + // UI mode that will be shown when the rods user switches mode from admin to + // user and vice-versa + public static final String UI_USER_MODE = "user"; + public static final String UI_ADMIN_MODE = "admin"; + + // CSV constants + private static final String METADATA_CSV_FILENAME_FORMAT = "metadata-search-result_%s_%s.csv"; + private static final String METADATA_CSV_DATE_FORMAT = "yyyyMMdd_HHmmss"; + private static final String METADATA_CSV_HEADER = "Filename;Path;Owner;Kind;Modified;Size;Matches\n"; + + @Value("${irods.zoneName}") + private String zoneName; + + // current page the user currently is (pagination) + private int currPage = 1; + + // current metadata search + List currSearch; + + private static final Logger logger = LoggerFactory.getLogger(MetadataController.class); + + @RequestMapping(value = "/") + public String index(final Model model, final HttpServletRequest request, + @RequestParam(value = "backFromCollections", required = false) final boolean backFromCollections) + throws DataGridException { + + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + String uiMode = (String) request.getSession().getAttribute("uiMode"); + if (uiMode == null || uiMode.isEmpty()) { + if (loggedUser.isAdmin()) { + uiMode = UI_ADMIN_MODE; + } else { + uiMode = UI_USER_MODE; + model.addAttribute("homePath", collectionService.getHomeDirectyForCurrentUser()); + model.addAttribute("publicPath", collectionService.getHomeDirectyForPublic()); + } + } + + if (backFromCollections) { + model.addAttribute("jsonMetadataSearch", jsonMetadataSearch); + } + model.addAttribute("uiMode", uiMode); + return "metadata/metadataDisplay"; + } + + @RequestMapping(value = "/search/", method = RequestMethod.POST) + @ResponseBody + public String searchByMetadata(@RequestParam(required = false) final String jsonMetadataSearch, + @RequestParam("draw") final int draw, @RequestParam("start") final int start, + @RequestParam("length") final int length) throws DataGridConnectionRefusedException { + + ObjectMapper mapper = new ObjectMapper(); + Map jsonResponse = new HashMap(); + jsonResponse.put("draw", String.valueOf(draw)); + jsonResponse.put("recordsTotal", String.valueOf(0)); + jsonResponse.put("recordsFiltered", String.valueOf(0)); + jsonResponse.put("data", new ArrayList()); + String jsonString = ""; + + try { + if (jsonMetadataSearch != null) { + currPage = (int) (Math.floor(start / length) + 1); + this.jsonMetadataSearch = jsonMetadataSearch; + } + + // Creating parser + JsonNode jsonObject = mapper.readTree(this.jsonMetadataSearch); + + currSearch = new ArrayList<>(); + + JsonNode attributes = jsonObject.get("attribute"); + JsonNode operators = jsonObject.get("operator"); + JsonNode values = jsonObject.get("value"); + JsonNode units = jsonObject.get("unit"); + + for (int i = 0; i < attributes.size(); i++) { + String attr = attributes.get(i).textValue(); + String val = values.get(i).textValue(); + String unit = units.get(i).textValue(); + String opt = operators.get(i).textValue(); + DataGridSearchOperatorEnum op = DataGridSearchOperatorEnum.valueOf(opt); + DataGridMetadataSearch ms = new DataGridMetadataSearch(attr, val, unit, op); + currSearch.add(ms); + } + + DataGridPageContext pageContext = new DataGridPageContext(); + List dgCollDataObjs = metadataService.findByMetadata(currSearch, + pageContext, currPage, length); + + jsonResponse.put("recordsTotal", String.valueOf(pageContext.getTotalNumberOfItems())); + jsonResponse.put("recordsFiltered", String.valueOf(pageContext.getTotalNumberOfItems())); + jsonResponse.put("data", dgCollDataObjs); + } catch (DataGridConnectionRefusedException e) { + throw e; + } catch (Exception e) { + logger.error("Could not search by metadata: ", e.getMessage()); + } + + try { + jsonString = mapper.writeValueAsString(jsonResponse); + } catch (JsonProcessingException e) { + logger.error("Could not parse hashmap in metadata search to json: {}", e.getMessage()); + } + + return jsonString; + } + + @RequestMapping(value = "/downloadCSVResults/") + public void exportSearchResultsToCSVFile(final HttpServletResponse response) + throws DataGridConnectionRefusedException, IOException { + + String loggedUser = loggedUserUtils.getLoggedDataGridUser().getUsername(); + String date = new SimpleDateFormat(METADATA_CSV_DATE_FORMAT).format(new Date()); + + String filename = String.format(METADATA_CSV_FILENAME_FORMAT, loggedUser, date); + + // Setting CSV Mime type + response.setContentType("text/csv"); + response.setHeader("Content-disposition", "attachment;filename=" + filename); + + ServletOutputStream outputStream = response.getOutputStream(); + + // Building search parameters lines + outputStream.print("Search condition #;Condition\n"); + + ListIterator resultEnumeration = currSearch.listIterator(); + while (resultEnumeration.hasNext()) { + String condition = resultEnumeration.next().toString(); + outputStream.print(String.format("%d;%s\n", resultEnumeration.nextIndex() + 1, condition)); + } + outputStream.print("\n"); + outputStream.flush(); + + // Executing query + DataGridPageContext pageContext = new DataGridPageContext(); + List dataGridCollectionAndDataObjects = metadataService + .findByMetadata(currSearch, pageContext, 1, Integer.MAX_VALUE); + + // Printing number of results + outputStream.print("Number of results\n"); + outputStream.print(String.format("%d\n", pageContext.getTotalNumberOfItems())); + outputStream.print("\n"); + + // Printing results + outputStream.print(METADATA_CSV_HEADER); + for (DataGridCollectionAndDataObject obj : dataGridCollectionAndDataObjects) { + + String kind = obj.isCollection() ? "collection" : "data object"; + + StringBuilder row = new StringBuilder(); + row.append(obj.getName() + ";"); + row.append(obj.getPath() + ";"); + row.append(obj.getOwner() + ";"); + row.append(kind + ";"); + row.append(obj.getModifiedAtFormattedForCSVReport() + ";"); + row.append(String.valueOf(obj.getSize()) + ";"); + row.append(String.valueOf(obj.getNumberOfMatches())); + row.append("\n"); + outputStream.print(row.toString()); + outputStream.flush(); + } + } + + @RequestMapping(value = "/getMetadata/") + public String getMetadata(final Model model, @RequestParam("path") final String path) + throws DataGridConnectionRefusedException { + + List metadataList = metadataService.findMetadataValuesByPath(path); + DataGridCollectionAndDataObject dgColObj = null; + + try { + dgColObj = collectionService.findByName(path); + permissionsService.resolveMostPermissiveAccessForUser(dgColObj, loggedUserUtils.getLoggedDataGridUser()); + } catch (DataGridException e) { + logger.error("Could not retrieve collection/dataobject from path: {}", path); + } + + model.addAttribute("permissionOnCurrentPath", collectionService.getPermissionsForPath(path)); + model.addAttribute("dataGridMetadataList", metadataList); + model.addAttribute("currentPath", path); + model.addAttribute("collectionAndDataObject", dgColObj); + return "metadata/metadataTable"; + } + + @RequestMapping(value = "/addMetadata/") + public String setMetadata(final Model model, @RequestParam("path") final String path, + @RequestParam("attribute") final String attribute, @RequestParam("value") final String value, + @RequestParam("unit") final String unit) throws DataGridConnectionRefusedException { + + if (metadataService.addMetadataToPath(path, attribute, value, unit)) { + model.addAttribute("addMetadataReturn", "success"); + } else { + model.addAttribute("addMetadataReturn", "failure"); + } + return getMetadata(model, path); + } + + @RequestMapping(value = "/modMetadata/") + public String modMetadata(final Model model, @RequestParam("path") final String path, + @RequestParam("oldAttribute") final String oldAttribute, @RequestParam("oldValue") final String oldValue, + @RequestParam("oldUnit") final String oldUnit, @RequestParam("newAttribute") final String newAttribute, + @RequestParam("newValue") final String newValue, @RequestParam("newUnit") final String newUnit) + throws DataGridConnectionRefusedException { + + if (metadataService.modMetadataFromPath(path, oldAttribute, oldValue, oldUnit, newAttribute, newValue, + newUnit)) { + model.addAttribute("modMetadataReturn", "success"); + } else { + model.addAttribute("modMetadataReturn", "failure"); + } + return getMetadata(model, path); + } + + @RequestMapping(value = "/delMetadataListPrototype") + public String delMetadataListPrototype(final HttpServletRequest request, final Model model) + throws DataGridConnectionRefusedException { + + String path = request.getParameter("path"); + Integer length = Integer.valueOf(request.getParameter("length")); + List> list = new ArrayList>(); + + for (int i = 0; i < length; i++) { + Map map = new HashMap(); + map.put("attribute", request.getParameter("params[" + i + "][attribute]")); + map.put("value", request.getParameter("params[" + i + "][value]")); + map.put("unit", request.getParameter("params[" + i + "][unit]")); + list.add(map); + } + + for (Map map : list) { + if (!metadataService.delMetadataFromPath(path, map.get("attribute"), map.get("value"), map.get("unit"))) { + model.addAttribute("delMetadataReturn", "failure"); + } + } + return getMetadata(model, path); + } + + @RequestMapping(value = "/exportToCSV") + public void exportToCSV(final HttpServletResponse response) throws IOException, DataGridConnectionRefusedException { + String filePath = collectionController.getCurrentPath(); + + List metadataList = metadataService.findMetadataValuesByPath(filePath); + + setReponseHeaderForCSVExport(response); + ServletOutputStream outputStream = response.getOutputStream(); + + outputStream.print("File path\n"); + outputStream.print(String.format("%s\n\n", filePath)); + + outputStream.print("Attribute Name,Value,Unit\n"); + for (DataGridMetadata m : metadataList) { + outputStream.print(String.format("%s,%s,%s\n", m.getAttribute(), m.getValue(), m.getUnit())); + } + outputStream.flush(); + } + + /* + * ************************************************************************* + * ***************************** PRIVATE METHODS *************************** + * ************************************************************************* + */ + + private void setReponseHeaderForCSVExport(final HttpServletResponse response) { + String date = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + + String filename = String.format("metadata_%s.csv", date); + + // Setting CSV Mime type + response.setContentType("text/csv"); + response.setHeader("Content-disposition", "attachment;filename=" + filename); + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/PermissionsController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/PermissionsController.java new file mode 100755 index 000000000..d9077b91b --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/PermissionsController.java @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.irods.jargon.core.exception.FileNotFoundException; +import org.irods.jargon.core.exception.JargonException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.SessionAttributes; + +import com.emc.metalnx.controller.utils.LoggedUserUtils; +import com.emc.metalnx.core.domain.entity.DataGridCollectionAndDataObject; +import com.emc.metalnx.core.domain.entity.DataGridFilePermission; +import com.emc.metalnx.core.domain.entity.DataGridGroup; +import com.emc.metalnx.core.domain.entity.DataGridGroupBookmark; +import com.emc.metalnx.core.domain.entity.DataGridGroupPermission; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserBookmark; +import com.emc.metalnx.core.domain.entity.DataGridUserPermission; +import com.emc.metalnx.core.domain.entity.enums.DataGridPermType; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.GroupBookmarkService; +import com.emc.metalnx.services.interfaces.GroupService; +import com.emc.metalnx.services.interfaces.PermissionsService; +import com.emc.metalnx.services.interfaces.UserBookmarkService; +import com.emc.metalnx.services.interfaces.UserService; + +@Controller +@SessionAttributes({ "currentPath", "groupsToAdd", "usersToAdd" }) +@RequestMapping(value = "/permissions") +public class PermissionsController { + + @Autowired + private UserService us; + + @Autowired + private GroupService gs; + + @Autowired + private GroupBookmarkService gBMS; + + @Autowired + private UserBookmarkService uBMS; + + @Autowired + private PermissionsService ps; + + @Autowired + private LoggedUserUtils luu; + + @Autowired + private CollectionService cs; + + private DataGridUser loggedUser; + + private HashMap usersToAdd; + private HashMap groupsToAdd; + + private static final String[] PERMISSIONS = { "NONE", "READ", "WRITE", "OWN" }; + private static final String[] PERMISSIONS_WITHOUT_NONE = { "READ", "WRITE", "OWN" }; + + private static final String REQUEST_OK = "OK"; + private static final String REQUEST_ERROR = "ERROR"; + private static final Logger logger = LoggerFactory.getLogger(PermissionsController.class); + + /** + * Finds the most restrictive permission on paths selected on the UI. + * + * @return string containing the most restrictive permission ("none", "read", + * "write", or "own") + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the grid + */ + @RequestMapping(value = "/findMostRestrictive/", method = RequestMethod.POST, produces = { "text/plain" }) + @ResponseBody + private String findMostRestrictivePermission(@RequestParam("paths[]") final String[] paths) + throws DataGridConnectionRefusedException { + + DataGridPermType mostRestrictivePermission = ps.findMostRestrictivePermission(paths); + + boolean isAdmin = luu.getLoggedDataGridUser().isAdmin(); + boolean isPermNone = mostRestrictivePermission.equals(DataGridPermType.NONE); + + if (isPermNone && isAdmin) { + mostRestrictivePermission = DataGridPermType.IRODS_ADMIN; + } + + return mostRestrictivePermission.toString().toLowerCase(); + } + + /** + * Gives permission details related to a collection or file that is passed as a + * parameter + * + * @param model + * @param path + * @return + * @throws DataGridConnectionRefusedException + * @throws JargonException + * @throws FileNotFoundException + */ + @RequestMapping(value = "/getPermissionDetails/") + public String getPermissionDetails(final Model model, @RequestParam("path") final String path) { + logger.debug("Getting permission info for {}", path); + + DataGridCollectionAndDataObject obj = null; + List permissions; + List groupPermissions; + List userPermissions; + List bookmarks; + List userBookmarks; + Set groupsWithBookmarks; + Set usersWithBookmarks; + boolean userCanModify = false; + boolean isCollection = false; + + try { + loggedUser = luu.getLoggedDataGridUser(); + + permissions = ps.getPathPermissionDetails(path); + groupPermissions = ps.getGroupsWithPermissions(permissions); + userPermissions = ps.getUsersWithPermissions(permissions); + + bookmarks = gBMS.findBookmarksOnPath(path); + userBookmarks = uBMS.findBookmarksOnPath(path); + userCanModify = loggedUser.isAdmin() || ps.canLoggedUserModifyPermissionOnPath(path); + + groupsWithBookmarks = new HashSet<>(); + for (DataGridGroupBookmark bookmark : bookmarks) { + groupsWithBookmarks.add(bookmark.getGroup().getGroupname()); + } + + usersWithBookmarks = new HashSet<>(); + for (DataGridUserBookmark userBookmark : userBookmarks) { + usersWithBookmarks.add(userBookmark.getUser().getUsername()); + } + + obj = cs.findByName(path); + ps.resolveMostPermissiveAccessForUser(obj, loggedUser); + } catch (Exception e) { + logger.error("Could not get permission details {}: {}", path, e.getMessage()); + + groupPermissions = new ArrayList<>(); + userPermissions = new ArrayList<>(); + groupsWithBookmarks = new HashSet<>(); + usersWithBookmarks = new HashSet<>(); + } + + model.addAttribute("usersWithBookmarks", usersWithBookmarks); + model.addAttribute("groupsWithBookmark", groupsWithBookmarks); + model.addAttribute("groupPermissions", groupPermissions); + model.addAttribute("userPermissions", userPermissions); + model.addAttribute("userCanModify", userCanModify); + model.addAttribute("permissions", PERMISSIONS); + model.addAttribute("permissionsWithoutNone", PERMISSIONS_WITHOUT_NONE); + model.addAttribute("collectionAndDataObject", obj); + model.addAttribute("isCollection", isCollection); + + return "permissions/permissionDetails :: permissionDetails"; + } + + @RequestMapping(value = "/changePermissionForGroup/") + @ResponseBody + public String changePermisionForGroup(@RequestParam("permissionData") final String permissionData, + @RequestParam("recursive") final boolean recursive) + throws DataGridConnectionRefusedException, JargonException { + return changePermissionForUserOrGroupOnPath(permissionData, recursive); + } + + @RequestMapping(value = "/changePermissionForUser/") + @ResponseBody + public String changePermisionForUser(@RequestParam("permissionData") final String permissionData, + @RequestParam("recursive") final boolean recursive) + throws DataGridConnectionRefusedException, JargonException { + return changePermissionForUserOrGroupOnPath(permissionData, recursive); + } + + /** + * Renders table that allows client to select which groups he wants to set new + * permissions to. + * + * @return + */ + @RequestMapping(value = "/getListOfGroupsForPermissionsCreation/") + public String getListOfGroupsForPermissionsCreation(final Model model) { + List groups = gs.findAll(); + + model.addAttribute("groups", groups); + model.addAttribute("groupsToAdd", groupsToAdd); + model.addAttribute("permissions", PERMISSIONS_WITHOUT_NONE); + + return "permissions/groupsForPermissionCreation"; + } + + /** + * Renders table that allows client to select which users he wants to set new + * permissions to. + * + * @return + */ + @RequestMapping(value = "/getListOfUsersForPermissionsCreation/") + public String getListOfUsersForPermissionsCreation(final Model model) { + List users = us.findAll(); + + model.addAttribute("users", users); + model.addAttribute("usersToAdd", usersToAdd); + model.addAttribute("permissions", PERMISSIONS_WITHOUT_NONE); + + return "permissions/usersForPermissionCreation"; + } + + @RequestMapping(value = "/addGroupPermissions/") + @ResponseBody + public String addGroupToCreationList(@RequestParam("permission") final String permission, + @RequestParam("groups") final String groups, @RequestParam("path") final String path, + @RequestParam("bookmark") final boolean bookmark, @RequestParam("recursive") final boolean recursive) + throws DataGridConnectionRefusedException { + + boolean operationResult = true; + String[] groupParts = groups.split(","); + DataGridPermType permType = DataGridPermType.valueOf(permission); + + loggedUser = luu.getLoggedDataGridUser(); + + for (String group : groupParts) { + if (gs.findByGroupname(group).isEmpty()) { + return REQUEST_ERROR; + } + } + for (String group : groupParts) { + operationResult &= ps.setPermissionOnPath(permType, group, recursive, loggedUser.isAdmin(), path); + } + + // Updating bookmarks for the recently-created permissions + if (bookmark) { + Set bookmarks = new HashSet(); + bookmarks.add(path); + + // Getting list of groups and updating bookmarks + List groupObjects = gs.findByGroupNameList(groupParts); + for (DataGridGroup g : groupObjects) { + gBMS.updateBookmarks(g, bookmarks, null); + } + } + + return operationResult ? REQUEST_OK : REQUEST_ERROR; + } + + @RequestMapping(value = "/addUserPermissions/") + @ResponseBody + public String addUserToCreationList(@RequestParam("permission") final String permission, + @RequestParam("users") final String users, @RequestParam("path") final String path, + @RequestParam("bookmark") final boolean bookmark, @RequestParam("recursive") final boolean recursive) + throws DataGridConnectionRefusedException { + + boolean operationResult = true; + String[] usernames = users.split(","); + DataGridPermType permType = DataGridPermType.valueOf(permission); + + loggedUser = luu.getLoggedDataGridUser(); + + for (String username : usernames) { + if (us.findByUsername(username).isEmpty()) { + return REQUEST_ERROR; + } + } + + for (String username : usernames) { + operationResult &= ps.setPermissionOnPath(permType, username, recursive, loggedUser.isAdmin(), path); + + // Updating bookmarks for the recently-created permissions + if (bookmark) { + Set bookmarks = new HashSet(); + bookmarks.add(path); + + // Getting list of users and updating bookmarks + List dataGridUsers = us.findByUsername(username); + if (dataGridUsers != null && !dataGridUsers.isEmpty()) { + uBMS.updateBookmarks(dataGridUsers.get(0), bookmarks, null); + } + } + } + + return operationResult ? REQUEST_OK : REQUEST_ERROR; + + } + + /* ********************************************************************* */ + /* **************************** PRIVATE METHOS ************************* */ + /* ********************************************************************* */ + + /** + * Sends the permissions change to the services layer and returns the result of + * the operation. + * + * @param permissionData + * @return + * @throws FileNotFoundException + * @throws JargonException + * @throws DataGridConnectionRefusedException + */ + private String changePermissionForUserOrGroupOnPath(final String permissionData, final boolean recursive) + throws DataGridConnectionRefusedException { + + // Getting information about the new permission to be applied and the path + // of the current object (collection or data object) + String[] permissionParts = permissionData.split("#"); + DataGridPermType newPermission = DataGridPermType.valueOf(permissionParts[0]); + String path = permissionParts[1]; + String userOrGroupName = permissionParts[2]; + + loggedUser = luu.getLoggedDataGridUser(); + + boolean permChanged = ps.setPermissionOnPath(newPermission, userOrGroupName, recursive, loggedUser.isAdmin(), + path); + + return permChanged ? REQUEST_OK : REQUEST_ERROR; + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/PreferencesController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/PreferencesController.java new file mode 100755 index 000000000..5eca9719e --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/PreferencesController.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.LocaleResolver; + +import com.emc.metalnx.controller.utils.LoggedUserUtils; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.modelattribute.preferences.UserPreferences; +import com.emc.metalnx.services.interfaces.UserService; + +@Controller +@RequestMapping(value = "/preferences") +public class PreferencesController { + + @Autowired + private UserService userService; + + @Autowired + private LocaleResolver localeResolver; + + @Autowired + LoggedUserUtils loggedUserUtils; + + // ui mode that will be shown when the rods user switches mode from admin to + // user and vice-versa + public static final String UI_USER_MODE = "user"; + public static final String UI_ADMIN_MODE = "admin"; + + @Value("${irods.zoneName}") + private String zoneName; + + @RequestMapping(value = "/") + public String index(final Model model, final HttpServletRequest request) { + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + String locale = loggedUser.getLocale(); + + String uiMode = (String) request.getSession().getAttribute("uiMode"); + + if (uiMode == null || uiMode.isEmpty()) { + if (loggedUser.isAdmin()) { + uiMode = UI_ADMIN_MODE; + } else { + uiMode = UI_USER_MODE; + } + } + + UserPreferences userPreferences = new UserPreferences(); + userPreferences.setLocaleLanguage(locale); + userPreferences.setForceFileOverwriting(loggedUser.isForceFileOverwriting()); + + model.addAttribute("preferences", userPreferences); + model.addAttribute("uiMode", uiMode); + + return "preferences/index"; + } + + @RequestMapping(value = "/action/") + public String action(final Model model, @ModelAttribute final UserPreferences preferences, + final HttpServletRequest request, final HttpServletResponse response) + throws DataGridConnectionRefusedException { + + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + loggedUser.setLocale(preferences.getLocaleLanguage()); + loggedUser.setForceFileOverwriting(preferences.isForceFileOverwriting()); + userService.modifyUser(loggedUser); + + localeResolver.setLocale(request, response, StringUtils.parseLocaleString(preferences.getLocaleLanguage())); + return "redirect:/dashboard/"; + } + + @RequestMapping(value = "/chat/") + public String webSocketTest() { + return "preferences/chat"; + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/RuleDeploymentController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/RuleDeploymentController.java new file mode 100755 index 000000000..2ccbcd0a9 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/RuleDeploymentController.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.context.WebApplicationContext; + +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@RequestMapping(value = "/rules") +public class RuleDeploymentController { + + private static final Logger logger = LoggerFactory.getLogger(RuleDeploymentController.class); + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String index() { + logger.info("Rules page"); + return "rules/rulesManagement"; + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/TemplateController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/TemplateController.java new file mode 100755 index 000000000..9cd694c04 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/TemplateController.java @@ -0,0 +1,804 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Scope; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.SessionAttributes; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; + +import com.emc.com.emc.metalnx.core.xml.MlxMetadataTemplate; +import com.emc.com.emc.metalnx.core.xml.MlxMetadataTemplates; +import com.emc.metalnx.controller.utils.LoggedUserUtils; +import com.emc.metalnx.core.domain.entity.DataGridTemplate; +import com.emc.metalnx.core.domain.entity.DataGridTemplateField; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateAttrException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateUnitException; +import com.emc.metalnx.core.domain.exceptions.DataGridTemplateValueException; +import com.emc.metalnx.core.domain.exceptions.DataGridTooLongTemplateNameException; +import com.emc.metalnx.modelattribute.enums.MetadataTemplateAccessType; +import com.emc.metalnx.modelattribute.metadatatemplate.MetadataTemplateForm; +import com.emc.metalnx.modelattribute.template.field.TemplateFieldForm; +import com.emc.metalnx.services.interfaces.CollectionService; +import com.emc.metalnx.services.interfaces.TemplateFieldService; +import com.emc.metalnx.services.interfaces.TemplateService; +import com.emc.metalnx.services.interfaces.UserService; + +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@SessionAttributes({ "selectedTemplates" }) +@RequestMapping(value = "/templates") +public class TemplateController { + + @Autowired + TemplateService templateService; + + @Autowired + TemplateFieldService templateFieldService; + + @Autowired + CollectionService collectionService; + + @Autowired + UserService userService; + + @Autowired + LoggedUserUtils loggedUserUtils; + + @Value("${irods.zoneName}") + private String zoneName; + + // contains the template fields to be added to a brand new template + private List addTemplateFields; + + // contains the template fields to be removed from a brand new template + private List removeTemplateFields; + + // Auxiliary structure to handle template selection + private Set selectedTemplates = new HashSet(); + + // UI mode that will be shown when the rods user switches mode from admin to + // user and vice-versa + public static final String UI_USER_MODE = "user"; + public static final String UI_ADMIN_MODE = "admin"; + + private static final Logger logger = LoggerFactory.getLogger(TemplateController.class); + + @RequestMapping(value = "/") + public String index(final Model model, final HttpServletRequest request) + throws DataGridConnectionRefusedException, DataGridException { + + addTemplateFields = new ArrayList(); + removeTemplateFields = new ArrayList(); + + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + String uiMode = (String) request.getSession().getAttribute("uiMode"); + + if (uiMode == null || uiMode.isEmpty()) { + if (loggedUser.isAdmin()) { + uiMode = UI_ADMIN_MODE; + } else { + uiMode = UI_USER_MODE; + model.addAttribute("homePath", collectionService.getHomeDirectyForCurrentUser()); + model.addAttribute("publicPath", collectionService.getHomeDirectyForPublic()); + } + } + selectedTemplates.clear(); + model.addAttribute("uiMode", uiMode); + return "template/templateManagement"; + } + + @RequestMapping(value = "/listTemplateFields", method = RequestMethod.POST) + public String listTemplateFields(final Model model, @RequestParam("template") final String template) { + updateAddTemplateFieldsList(); + + List dataGridTemplateFields = templateService.listTemplateFields(template); + List templateFields = this.mapDataGridTempToFieldForm(dataGridTemplateFields); + DataGridTemplate metadataTemplate = templateService.findByName(template); + + if (addTemplateFields != null) { + templateFields.addAll(addTemplateFields); + } + if (removeTemplateFields != null) { + templateFields.removeAll(removeTemplateFields); + } + + model.addAttribute("metadataTemplate", metadataTemplate); + model.addAttribute("templateFields", templateFields); + model.addAttribute("resultSize", templateFields.size()); + model.addAttribute("foundTemplateFields", templateFields.size() > 0); + + return "template/templateFieldList"; + } + + @RequestMapping(value = "/listTemplateFieldsForCollections", method = RequestMethod.POST) + public String listTemplateFieldsForCollections(final Model model, + @RequestParam("templateIDsList") final long[] templateIDsList) { + MetadataTemplateForm templateForm = new MetadataTemplateForm(); + List templateFields = new ArrayList(); + List dataGridTemplateFields = new ArrayList(); + + if (templateIDsList.length > 0) { + for (long id : templateIDsList) { + dataGridTemplateFields.addAll(templateService.listTemplateFields(id)); + } + templateFields = this.mapDataGridTempToFieldForm(dataGridTemplateFields); + } + + model.addAttribute("templateForm", templateForm); + model.addAttribute("requestMapping", "/collections/applyTemplatesToCollections/"); + model.addAttribute("templateFields", templateFields); + model.addAttribute("resultSize", templateFields.size()); + model.addAttribute("foundTemplateFields", templateFields.size() > 0); + + return "collections/templateFieldListForCollections"; + } + + @RequestMapping(value = "/listTemplatesForCollections", method = RequestMethod.POST) + public String listTemplatesForCollections(final Model model) { + findAllTemplates(model); + return "collections/templateListForCollections :: templateList"; + } + + @RequestMapping(value = "/findAll/") + public String findAll(final Model model) { + findAllTemplates(model); + return "template/templateList :: templateList"; + } + + @RequestMapping(value = "/find/{templateName}") + public String findTemplate(final Model model, @PathVariable final String templateName) { + List templates = templateService.findByQueryString(templateName); + + model.addAttribute("templates", templates); + model.addAttribute("foundTemplates", templates.size() >= 0); + model.addAttribute("resultSize", templates.size()); + model.addAttribute("queryString", templateName); + + return "template/templateList :: templateList"; + } + + @RequestMapping(value = "add/") + public String newTemplate(final Model model, final HttpServletRequest request) { + MetadataTemplateForm templateForm = new MetadataTemplateForm(); + TemplateFieldForm templateFieldForm = new TemplateFieldForm(); + + if (addTemplateFields == null) { + addTemplateFields = new ArrayList(); + } + + templateForm.setOwner(loggedUserUtils.getLoggedDataGridUser().getUsername()); + + model.addAttribute("uiMode", request.getSession().getAttribute("uiMode")); + model.addAttribute("accessTypes", MetadataTemplateAccessType.values()); + model.addAttribute("metadataTemplateForm", templateForm); + model.addAttribute("templateFieldForm", templateFieldForm); + model.addAttribute("requestMapping", "/templates/add/action/"); + model.addAttribute("requestMappingForTemplateField", "/templates/addFieldToCurrentTemplate"); + + return "template/templateForm"; + } + + @RequestMapping(value = "add/action/") + public String addNewTemplate(final Model model, @ModelAttribute final MetadataTemplateForm templateForm, + final RedirectAttributes redirectAttributes) { + DataGridTemplate newTemplate = null; + + try { + newTemplate = new DataGridTemplate(); + newTemplate.setTemplateName(templateForm.getTemplateName()); + newTemplate.setDescription(templateForm.getDescription()); + newTemplate.setUsageInformation(templateForm.getUsageInformation()); + newTemplate.setOwner(loggedUserUtils.getLoggedDataGridUser().getUsername()); + newTemplate.setAccessType(templateForm.getAccessType().toString()); + + long templateID = templateService.createTemplate(newTemplate); + + if (templateID > 0) { + redirectAttributes.addFlashAttribute("templateAddedSuccessfully", newTemplate.getTemplateName()); + newTemplate.setId(templateID); + + // adding all fields to the template + for (TemplateFieldForm tempFieldForm : addTemplateFields) { + DataGridTemplateField dataGridTempField = mapTempFieldFormToDataGridTemp(tempFieldForm); + dataGridTempField.setTemplate(newTemplate); + templateFieldService.createTemplateField(dataGridTempField); + } + + // reseting the temporary fields to be added and removed from a + // template + addTemplateFields = new ArrayList(); + removeTemplateFields = new ArrayList(); + + return "redirect:/templates/"; + } + } catch (DataGridTooLongTemplateNameException e) { + redirectAttributes.addFlashAttribute("templateNotAddedSuccessfully", true); + redirectAttributes.addFlashAttribute("tooLongTemplateName", true); + } catch (Exception e) { + redirectAttributes.addFlashAttribute("templateNotAddedSuccessfully", true); + } + + return "redirect:/templates/add/"; + } + + @RequestMapping(value = "modify/") + public String showModifyTemplate(final Model model, final HttpServletRequest request) { + // DataGridTemplate template = + // templateService.findById(selectedTemplates.iterator().next()); + DataGridTemplate template = templateService.findById(Long.parseLong(request.getParameter("templateId"))); + + if (template == null) { + return "redirect:/templates/"; + } + + MetadataTemplateForm templateForm = new MetadataTemplateForm(); + templateForm.setId(template.getId()); + templateForm.setTemplateName(template.getTemplateName()); + templateForm.setDescription(template.getDescription()); + templateForm.setUsageInformation(template.getUsageInformation()); + templateForm.setOwner(template.getOwner()); + templateForm.setAccessType(template.getAccessType()); + templateForm.setVersion(template.getVersion()); + + TemplateFieldForm templateFieldForm = new TemplateFieldForm(); + + model.addAttribute("uiMode", request.getSession().getAttribute("uiMode")); + model.addAttribute("accessTypes", MetadataTemplateAccessType.values()); + model.addAttribute("metadataTemplateForm", templateForm); + model.addAttribute("templateFieldForm", templateFieldForm); + model.addAttribute("requestMapping", "/templates/modify/action/"); + + return "template/templateForm"; + } + + @RequestMapping(value = "modify/action/") + public String modifyTemplate(final Model model, @ModelAttribute final MetadataTemplateForm templateForm, + final RedirectAttributes redirectAttributes) { + + DataGridTemplate template = null; + + try { + template = templateService.findById(templateForm.getId()); + + if (template == null) { + throw new Exception("Cannot modify a non-existent template"); + } + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + if (!template.getOwner().equalsIgnoreCase(loggedUser.getUsername())) { + throw new Exception("Cannot modify a template beloging to another user"); + } + + template.setTemplateName(templateForm.getTemplateName()); + template.setDescription(templateForm.getDescription()); + template.setUsageInformation(templateForm.getUsageInformation()); + template.setAccessType(templateForm.getAccessType().toString()); + + List positions = templateForm.getAvuPositions(); + List attributes = templateForm.getAvuAttributes(); + List values = templateForm.getAvuValues(); + List units = templateForm.getAvuUnits(); + + if (attributes != null) { + for (int i = 0; i < attributes.size(); i++) { + for (int j = i + 1; j < attributes.size(); j++) { + if (attributes.get(i).equals(attributes.get(j)) && !values.get(i).isEmpty() + && values.get(i).equals(values.get(j)) && units.get(i).equals(units.get(j))) { + redirectAttributes.addFlashAttribute("repeatedAVU", true); + return "redirect:/templates/modify/"; + } + } + } + } + + if (positions != null) { + for (int i = 0; i < positions.size(); i++) { + String position = positions.get(i); + if (position.contains("mod_pos_")) { + addTemplateFields.get(Integer.parseInt(position.replace("mod_pos_", ""))) + .setAttribute(attributes.get(i)); + addTemplateFields.get(Integer.parseInt(position.replace("mod_pos_", ""))) + .setValue(values.get(i)); + addTemplateFields.get(Integer.parseInt(position.replace("mod_pos_", ""))).setUnit(units.get(i)); + } else if (position.contains("mod_id_")) { + templateFieldService.modifyTemplateField(Long.parseLong(position.replace("mod_id_", "")), + attributes.get(i), values.get(i), units.isEmpty() ? "" : units.get(i)); + template.setModified(true); + } + } + } + + // adding all fields to the template + for (TemplateFieldForm tempFieldForm : addTemplateFields) { + DataGridTemplateField dataGridTempField = mapTempFieldFormToDataGridTemp(tempFieldForm); + dataGridTempField.setTemplate(template); + templateFieldService.createTemplateField(dataGridTempField); + template.setModified(true); + } + + Set currentTempFields = template.getFields(); + + // removing fields from the template + for (TemplateFieldForm tempFieldForm : removeTemplateFields) { + DataGridTemplateField dataGridTempField = mapTempFieldFormToDataGridTemp(tempFieldForm); + dataGridTempField.setTemplate(template); + template.setModified(true); + + // removing template fields from template + if (currentTempFields.remove(dataGridTempField)) { + templateFieldService.deleteTemplateField(dataGridTempField); + logger.debug("Template removed from memory and database"); + } + // template field wasn't removed + else { + throw new Exception("Could not removed template field from memory."); + } + } + + template.setFields(currentTempFields); + templateService.modifyTemplate(template); + + // reseting the temporary fields to be added and removed from a template + addTemplateFields = new ArrayList(); + removeTemplateFields = new ArrayList(); + + redirectAttributes.addFlashAttribute("templateModifiedSuccessfully", template.getTemplateName()); + + selectedTemplates.clear(); + } catch (Exception e) { + logger.error("Could not modify template {}.", templateForm.getTemplateName()); + redirectAttributes.addFlashAttribute("templateNotModifiedSuccessfully", templateForm.getTemplateName()); + } + + return "redirect:/templates/"; + + } + + @RequestMapping(value = "delete/") + public String deleteTemplate(final RedirectAttributes redirectAttributes) { + + boolean deletionSuccessful = true; + + for (Long templateId : selectedTemplates) { + logger.debug("Deleting template [{}]", templateId); + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + if (templateService.findById(templateId).getOwner().equalsIgnoreCase(loggedUser.getUsername())) { + deletionSuccessful &= templateService.deleteTemplate(templateId); + } + } + + redirectAttributes.addFlashAttribute("templateRemovedSuccessfully", deletionSuccessful); + selectedTemplates.clear(); + + return "redirect:/templates/"; + } + + @RequestMapping(value = "/addFieldToCurrentTemplate") + public String addFieldToCurrentTemplate(final Model model, + @ModelAttribute final TemplateFieldForm templateFieldForm) { + updateAddTemplateFieldsList(); + + boolean isAddFieldsEmpty = false; + + try { + List existingTemplateFields = templateService + .listTemplateFields(templateFieldForm.getTemplateName()); + + DataGridTemplateField newDataGridField = mapTempFieldFormToDataGridTemp(templateFieldForm); + + List templateFields = new ArrayList(); + TemplateFieldForm newField = this.mapDataGridTempToFieldForm(newDataGridField, addTemplateFields.size()); + templateFields.add(newField); + + if (addTemplateFields.isEmpty()) { + isAddFieldsEmpty = true; + } + + // prevent an AVU from being added twice to a template + if (!addTemplateFields.contains(newField) + || addTemplateFields.contains(newField) && newField.getValue().equals("")) { + addTemplateFields.add(newField.getFormListPosition(), newField); + } + + model.addAttribute("templateFields", templateFields); + model.addAttribute("resultSize", templateFields.size()); + model.addAttribute("foundTemplateFields", templateFields.size() > 0); + + if (isAddFieldsEmpty && existingTemplateFields.isEmpty()) { + return "template/templateFieldList"; + } + } catch (DataGridTemplateAttrException e) { + logger.error(e.getMessage()); + } catch (DataGridTemplateValueException e) { + logger.error(e.getMessage()); + } catch (DataGridTemplateUnitException e) { + logger.error(e.getMessage()); + } + + return "template/templateFieldList :: avuRow"; + } + + @RequestMapping(value = "/removeFieldFromDB") + @ResponseStatus(value = HttpStatus.OK) + public void removeFieldFromDB(@RequestParam("templateFieldsIDList") final long[] templateFieldsIDList) { + for (long templateFieldID : templateFieldsIDList) { + DataGridTemplateField dataGridField = templateFieldService.findById(templateFieldID); + TemplateFieldForm field = this.mapDataGridTempToFieldForm(dataGridField); + + if (addTemplateFields.contains(field)) { + addTemplateFields.remove(field); + } + + // adding the field to the list that is needed to be removed from a template + if (!removeTemplateFields.contains(field)) { + removeTemplateFields.add(field); + } + } + } + + @RequestMapping(value = "/removeFieldFromTemplate") + @ResponseStatus(value = HttpStatus.OK) + public void removeFieldFromTemplate(@RequestParam("templateFieldsPosList") final int[] templateFieldsPosList) { + int addTemplateFieldsSize = addTemplateFields.size(); + + for (int templateFieldPos : templateFieldsPosList) { + if (templateFieldPos >= 0 && templateFieldPos < addTemplateFieldsSize) { + addTemplateFields.remove(templateFieldPos); + } + } + } + + @RequestMapping(value = "/exportTemplatesToXMLFile/") + public void exportTemplateListToXMLFile(final HttpServletResponse response) { + try { + setReponseHeaderForXmlExport(response); + + // Creating marshaller mechanism + JAXBContext jaxbContext = JAXBContext.newInstance(MlxMetadataTemplates.class); + Marshaller m = jaxbContext.createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + + // Creating XML data structure for exporting + MlxMetadataTemplates ts = new MlxMetadataTemplates(); + + for (Long templateId : selectedTemplates) { + + // Getting template data + DataGridTemplate template = templateService.findById(Long.valueOf(templateId)); + + // Mapping DB entity to XML entity + MlxMetadataTemplate t = templateService.mapDataGridTemplateToXml(template); + ts.getTemplates().add(t); + } + + // Marshalling and flushing stream + m.marshal(ts, response.getOutputStream()); + response.getOutputStream().flush(); + + selectedTemplates.clear(); + + } catch (JAXBException | IOException e) { + logger.error("Could not export templates using metadata", e); + } + } + + @RequestMapping(value = "/import/", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + @ResponseBody + public String importXMLFile(final Model model, final HttpServletRequest request, + final RedirectAttributes redirect) { + + String responseString = "ok"; + + if (request instanceof MultipartHttpServletRequest) { + + MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; + List multipartFiles = multipartRequest.getFiles("file"); + String prefix = multipartRequest.getParameter("prefix"); + String suffix = multipartRequest.getParameter("suffix"); + + try { + String username = loggedUserUtils.getLoggedDataGridUser().getUsername(); + boolean result = templateService.importXmlMetadataTemplate(multipartFiles.get(0).getInputStream(), + username, prefix, suffix); + + if (!result) { + responseString = "partial"; + } + + } catch (JAXBException | IOException | DataGridException e) { + logger.error("Could not import metadata templates", e); + responseString = "error"; + } + + } + return responseString; + } + + /* + * ************************************************************************ + * ******************** HANDLING SESSION VARIABLES ************************ + * ************************************************************************ + */ + + @RequestMapping(value = "/selectTemplate/", method = RequestMethod.POST) + @ResponseBody + public boolean selectTemplate(@RequestParam("id") final String id) { + Long idLong = Long.valueOf(id); + if (!selectedTemplates.contains(idLong)) { + selectedTemplates.add(idLong); + } + + return canCurrentUserRemoveSelectedTemplates(); + } + + @RequestMapping(value = "/unselectTemplate/", method = RequestMethod.POST) + @ResponseBody + public boolean unselectTemplate(@RequestParam("id") final String id) { + Long idLong = Long.valueOf(id); + if (selectedTemplates.contains(idLong)) { + selectedTemplates.remove(idLong); + } + + return canCurrentUserRemoveSelectedTemplates(); + } + + /* + * ***************************************************************************** + * *************** ******************************** VALIDATION + * *************************************** + * ***************************************************************************** + * *************** + */ + + /** + * Validates a template name + * + * @param templateName + * @return True, if the template name can be used. False, otherwise. + */ + @ResponseBody + @RequestMapping(value = "isValidTemplateName/{templateName}/", method = RequestMethod.GET) + public String isValidUsername(@PathVariable final String templateName) { + + if (templateName.compareTo("") != 0) { + // if no users are found with this username, it means this username can be used + DataGridTemplate template = templateService.findByName(templateName); + return template == null ? "true" : "false"; + } + + return "false"; + } + + /* + * ************************************************************************* + * ***************************** PRIVATE METHODS *************************** + * ************************************************************************* + */ + + private void setReponseHeaderForXmlExport(final HttpServletResponse response) { + String loggedUser = loggedUserUtils.getLoggedDataGridUser().getUsername(); + String date = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + + String filename = String.format("template_%s_%s.xml", loggedUser, date); + + // Setting CSV Mime type + response.setContentType("text/xml"); + response.setHeader("Content-disposition", "attachment;filename=" + filename); + } + + private void findAllTemplates(final Model model) { + + List templates = new ArrayList(); + String loggedUser = loggedUserUtils.getLoggedDataGridUser().getUsername(); + + templates = templateService.listPublicTemplates(); + templates.addAll(templateService.listPrivateTemplatesByUser(loggedUser)); + + Collections.sort(templates); + + model.addAttribute("templates", templates); + model.addAttribute("resultSize", templates.size()); + model.addAttribute("foundTemplates", templates.size() >= 0); + } + + /** + * Updates the form list position of each element of the addTemplateFields array + * list. + */ + private void updateAddTemplateFieldsList() { + if (addTemplateFields != null) { + // updating form list position once an element was removed from the + // array + int i = 0; + for (TemplateFieldForm templateFieldForm : addTemplateFields) { + templateFieldForm.setFormListPosition(i); + i++; + } + } + } + + /** + * Maps a single instance of DataGridTemplateField into a TemplateFieldForm + * object + * + * @param dataGridTempField + * DataGridTemplateField object + * @return TemplateFieldForm object + */ + private TemplateFieldForm mapDataGridTempToFieldForm(final DataGridTemplateField dataGridTempField) { + if (dataGridTempField == null) { + return null; + } + + TemplateFieldForm tempField = new TemplateFieldForm(); + tempField.setAttribute(dataGridTempField.getAttribute()); + tempField.setValue(dataGridTempField.getValue()); + tempField.setUnit(dataGridTempField.getUnit()); + tempField.setStartRange(dataGridTempField.getStartRange()); + tempField.setEndRange(dataGridTempField.getEndRange()); + tempField.setOrder(dataGridTempField.getOrder()); + tempField.setId(dataGridTempField.getId()); + + if (dataGridTempField.getTemplate() != null) { + tempField.setTemplateName(dataGridTempField.getTemplate().getTemplateName()); + } + + return tempField; + } + + /** + * Maps a single instance of DataGridTemplateField into a TemplateFieldForm + * object + * + * @param dataGridTempField + * DataGridTemplateField object + * @param position + * DataGridTemplateField object position into the memory array list + * of fields to be added to a template + * @return TemplateFieldForm object + */ + private TemplateFieldForm mapDataGridTempToFieldForm(final DataGridTemplateField dataGridTempField, + final int position) { + if (dataGridTempField == null) { + return null; + } + + TemplateFieldForm tempField = new TemplateFieldForm(); + tempField.setAttribute(dataGridTempField.getAttribute()); + tempField.setValue(dataGridTempField.getValue()); + tempField.setUnit(dataGridTempField.getUnit()); + tempField.setStartRange(dataGridTempField.getStartRange()); + tempField.setEndRange(dataGridTempField.getEndRange()); + tempField.setOrder(dataGridTempField.getOrder()); + tempField.setFormListPosition(position); + tempField.setId(dataGridTempField.getId()); + + if (dataGridTempField.getTemplate() != null) { + tempField.setTemplateName(dataGridTempField.getTemplate().getTemplateName()); + } + + return tempField; + } + + /** + * Maps a list of DataGridTemplateField objects into a list of TemplateFieldForm + * objects + * + * @param tempFields + * list of data grid template objects + * @return list of TemplateFieldForm objects + */ + private List mapDataGridTempToFieldForm(final List tempFields) { + List tempFieldFormList = new ArrayList(); + + if (tempFields == null || tempFields.isEmpty()) { + return tempFieldFormList; + } + + TemplateFieldForm tempField = null; + + int position = 0; + for (DataGridTemplateField dataGridTemplateField : tempFields) { + tempField = this.mapDataGridTempToFieldForm(dataGridTemplateField, position); + tempFieldFormList.add(tempField); + } + + return tempFieldFormList; + } + + /** + * Map a Template Field form object into a DataGridTemplateField object + * + * @param tempFieldForm + * template field form object to be mapped + * @return DataGridTemplateField object + * @throws DataGridTemplateAttrException + * @throws DataGridTemplateValueException + * @throws DataGridTemplateUnitException + */ + private DataGridTemplateField mapTempFieldFormToDataGridTemp(final TemplateFieldForm tempFieldForm) + throws DataGridTemplateAttrException, DataGridTemplateValueException, DataGridTemplateUnitException { + + if (tempFieldForm == null) { + return null; + } + + DataGridTemplateField templateField = new DataGridTemplateField(); + templateField.setAttribute(tempFieldForm.getAttribute()); + templateField.setValue(tempFieldForm.getValue()); + templateField.setUnit(tempFieldForm.getUnit()); + templateField.setStartRange(tempFieldForm.getStartRange()); + templateField.setEndRange(tempFieldForm.getEndRange()); + templateField.setOrder(tempFieldForm.getOrder()); + + if (tempFieldForm.getId() != null) { + templateField.setId(tempFieldForm.getId()); + } + + return templateField; + } + + /** + * Checks if the currently logged user can delete the list of selected templates + * + * @return true it is possible to delete selected templates, false otherwise + */ + private boolean canCurrentUserRemoveSelectedTemplates() { + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + for (Long templateId : selectedTemplates) { + if (!templateService.findById(templateId).getOwner().equalsIgnoreCase(loggedUser.getUsername())) { + return false; + } + } + return true; + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/TicketClientController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/TicketClientController.java new file mode 100755 index 000000000..3a350ca1c --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/TicketClientController.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import com.emc.metalnx.controller.utils.LoggedUserUtils; +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketDownloadException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketFileNotFoundException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketInvalidUserException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketUploadException; +import com.emc.metalnx.services.interfaces.TicketClientService; + +/** + * Controller that will handle anonymous access to collections and data objects + * using tickets. + */ +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@RequestMapping(value = "/ticketclient") +public class TicketClientController { + private static final String APPLICATION_OCTET_STREAM = "text/octet-stream"; + private static final String HEADER_FORMAT = "attachment;filename=\"%s\""; + private static final Logger logger = LoggerFactory.getLogger(TicketClientController.class); + private static final String IRODS_PATH_SEPARATOR = "/"; + private static final String CONTENT_DISPOSITION = "Content-Disposition"; + + @Autowired + private TicketClientService ticketClientService; + + @Autowired + private LoggedUserUtils loggedUserUtils; + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String index(final Model model, @RequestParam("ticketstring") final String ticketString, + @RequestParam("ticketpath") final String path) throws DataGridConnectionRefusedException { + logger.info("Accessing ticket {} on {}", ticketString, path); + String objName = path.substring(path.lastIndexOf(IRODS_PATH_SEPARATOR) + 1, path.length()); + model.addAttribute("objName", objName); + model.addAttribute("ticketString", ticketString); + model.addAttribute("path", path); + + DataGridUser loggedUser = loggedUserUtils.getLoggedDataGridUser(); + + return loggedUser == null ? "tickets/ticketclient" : "tickets/ticketAuthAccess"; + } + + @RequestMapping(value = "/invaliduser", method = RequestMethod.GET) + public String invalidUser() { + return "tickets/ticketinvaliduser"; + } + + @RequestMapping(value = "/{ticketstring}", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + public void upload(@PathVariable("ticketstring") final String ticketString, final HttpServletRequest request) + throws DataGridConnectionRefusedException, DataGridTicketUploadException, IOException, + DataGridTicketInvalidUserException { + logger.info("Uploading files using ticket: {}", ticketString); + + if (!(request instanceof MultipartHttpServletRequest)) { + logger.error("Request is not a multipart request. Stop."); + return; + } + + MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; + MultipartFile multipartFile = multipartRequest.getFile("file"); + String destPath = multipartRequest.getParameter("path"); + + File file = multipartToFile(multipartFile); + ticketClientService.transferFileToIRODSUsingTicket(ticketString, file, destPath); + } + + @RequestMapping(value = "/{ticketstring}", method = RequestMethod.GET) + public void download(@PathVariable("ticketstring") final String ticketString, + @RequestParam("path") final String path, final HttpServletResponse response) + throws DataGridConnectionRefusedException, DataGridTicketFileNotFoundException, IOException, + DataGridTicketInvalidUserException, DataGridTicketDownloadException { + logger.info("Getting files using ticket: {}", ticketString); + + File file = ticketClientService.getFileFromIRODSUsingTicket(ticketString, path); + + if (file != null) { + setResponseStream(response, file); + } + + ticketClientService.deleteTempTicketDir(); + } + + /** + * Sets the HTTP response stream. + * + * @param response + * HTTP response + * @param file + * file to be sent back with the HTTP response + * @throws IOException + * if Metalnx cannot stream the file + */ + private void setResponseStream(final HttpServletResponse response, final File file) throws IOException { + String filename = file.getName(); + response.setContentType(APPLICATION_OCTET_STREAM); + response.setHeader(CONTENT_DISPOSITION, String.format(HEADER_FORMAT, filename)); + FileCopyUtils.copy(getInputStream(file), response.getOutputStream()); // takes care of closing streams + } + + /** + * Gets an input stream from a file within a directory + * + * @param file + * file to get the stream from + * @return stream for the file + * @throws FileNotFoundException + */ + private InputStream getInputStream(final File file) throws FileNotFoundException { + return new FileInputStream(file); + } + + /** + * Converts a multipart file comming from an HTTP request into a File instance. + * + * @param multipartFile + * file uploaded + * @return File instance + * @throws IllegalStateException + * @throws IOException + */ + private File multipartToFile(final MultipartFile multipartFile) throws IllegalStateException, IOException { + File convFile = new File(multipartFile.getOriginalFilename()); + multipartFile.transferTo(convFile); + return convFile; + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/TicketController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/TicketController.java new file mode 100755 index 000000000..9ba832509 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/TicketController.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.WebApplicationContext; + +import com.emc.metalnx.controller.utils.LoggedUserUtils; +import com.emc.metalnx.core.domain.entity.DataGridTicket; +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketException; +import com.emc.metalnx.core.domain.exceptions.DataGridTicketNotFoundException; +import com.emc.metalnx.services.interfaces.TicketService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@RequestMapping(value = "/tickets") +public class TicketController { + private static final Logger logger = LoggerFactory.getLogger(TicketController.class); + + @Autowired + private TicketService ticketService; + + @Autowired + private LoggedUserUtils loggedUserUtils; + + @RequestMapping(value = "/", method = RequestMethod.GET) + public String index() throws DataGridConnectionRefusedException { + logger.info("Get tickets page"); + return "tickets/tickets"; + } + + @RequestMapping(value = "/ticketForm", method = RequestMethod.GET) + public String createTicketForm(final Model model, + @RequestParam(value = "ticketstring", required = false) final String ticketString) + throws DataGridConnectionRefusedException, DataGridTicketNotFoundException { + + DataGridTicket ticket; + + if (ticketString != null && !ticketString.isEmpty()) { + ticket = ticketService.find(ticketString); + } else { + ticket = new DataGridTicket(); + } + + model.addAttribute("ticket", ticket); + model.addAttribute("requestMapping", "tickets/"); + return "tickets/ticketForm"; + } + + /** + * Finds all tickets in the grid. + * + * @return List of tickets in JSON + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the grid + */ + @RequestMapping(value = "/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public String findAll() throws DataGridConnectionRefusedException, JsonProcessingException { + logger.info("Find all tickets"); + List tickets = ticketService.findAll(); + + Map ticketsAsJSON = new HashMap<>(); + ticketsAsJSON.put("data", tickets); + + return new ObjectMapper().writeValueAsString(ticketsAsJSON); + } + + /** + * Finds a specific ticket in the grid by its id or string + * + * @param ticketId + * ticket id or string + * @return Ticket as JSON + * @throws DataGridConnectionRefusedException + * if Metalnx cannot connect to the grid + */ + @RequestMapping(value = "/{ticketid}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public ResponseEntity find(@PathVariable("ticketid") final String ticketId) + throws DataGridConnectionRefusedException, DataGridTicketNotFoundException { + logger.info("Find ticket by its ID or String"); + DataGridTicket dgTicket = ticketService.find(ticketId); + return new ResponseEntity<>(dgTicket, HttpStatus.OK); + } + + @RequestMapping(value = "/{ticketId}", method = RequestMethod.DELETE) + public ResponseEntity deleteTicket(@PathVariable final String ticketId) + throws DataGridConnectionRefusedException { + logger.info("Delete ticket by its ID or String"); + boolean ticketDeleted = ticketService.delete(ticketId); + + if (!ticketDeleted) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @RequestMapping(value = "/", method = RequestMethod.DELETE, consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseStatus(HttpStatus.NO_CONTENT) + public ResponseEntity bulkDeleteTickets(@RequestBody final List ticketStrings) + throws DataGridConnectionRefusedException { + logger.info("Delete tickets of user: {}", loggedUserUtils.getLoggedDataGridUser().getUsername()); + boolean ticketsDeleted = ticketService.bulkDelete(ticketStrings); + if (!ticketsDeleted) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @RequestMapping(value = "/", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.CREATED) + @ResponseBody + public DataGridTicket createTicket(@RequestBody final DataGridTicket ticket) + throws DataGridConnectionRefusedException, DataGridTicketException { + logger.info("Create new ticket"); + ticket.setOwner(loggedUserUtils.getLoggedDataGridUser().getUsername()); + ticketService.create(ticket); + return ticket; + } + + @RequestMapping(value = "/", method = RequestMethod.PUT) + @ResponseStatus(value = HttpStatus.NO_CONTENT) + public void modifyTicket(@RequestBody final DataGridTicket ticket) + throws DataGridConnectionRefusedException, DataGridTicketException { + logger.info("Modify ticket"); + ticketService.modify(ticket); + } + + @RequestMapping(value = "/validatehost", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public HostInfo validateTicketHostname(@RequestParam("hostname") final String hostname) + throws UnknownHostException { + logger.info("Validating ticket hostname {}", hostname); + return new HostInfo(hostname); + } +} + +class HostInfo { + + private String hostname; + private String ip; + + HostInfo(final String hostname) throws UnknownHostException { + InetAddress address = InetAddress.getByName(hostname); + this.hostname = hostname; + ip = address.getHostAddress(); + } + + public String getHostname() { + return hostname; + } + + public String getIp() { + return ip; + } +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/UploadController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/UploadController.java new file mode 100755 index 000000000..66b963bbf --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/UploadController.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import javax.servlet.http.HttpServletRequest; + +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import com.emc.metalnx.core.domain.exceptions.DataGridConnectionRefusedException; +import com.emc.metalnx.core.domain.exceptions.DataGridException; +import com.emc.metalnx.core.domain.exceptions.DataGridReplicateException; +import com.emc.metalnx.core.domain.exceptions.DataGridRuleException; +import com.emc.metalnx.services.interfaces.RuleDeploymentService; +import com.emc.metalnx.services.interfaces.UploadService; + +@Controller +@Scope(WebApplicationContext.SCOPE_SESSION) +@RequestMapping(value = "/upload") +public class UploadController { + + private static final String WARNING = "warning"; + private static final String FATAL = "fatal"; + private static final Logger logger = LoggerFactory.getLogger(UploadController.class); + public static final String METADATA_EXTRACTION_FAILED_MSG = "Metadata extraction failed."; + + @Autowired + private CollectionController cc; + + @Autowired + private UploadService us; + + @Autowired + private RuleDeploymentService ruleDeploymentService; + + /** + * Sets the HTTP response text and status for an upload request. + * + * @param uploadMessage + * string that identifies what kind of uploadMessage happened during + * an upload + * @return status OK and response text "Upload Successful" if the upload has no + * errors. Otherwise, it returns an HTTP status 500 and response text + * with the corresponding uploadMessage. + */ + private ResponseEntity getUploadResponse(final String uploadMessage, final String errorType) { + HttpStatus status = HttpStatus.OK; + String path = cc.getCurrentPath(); + + JSONObject jsonUploadMsg = new JSONObject(); + + try { + jsonUploadMsg.put("path", path); + jsonUploadMsg.put("msg", uploadMessage); + + if (!errorType.isEmpty()) { + jsonUploadMsg.put("errorType", errorType); + status = HttpStatus.INTERNAL_SERVER_ERROR; + } + } catch (JSONException e) { + logger.error("Could not create JSON object for upload response: {]", e.getMessage()); + } + + return new ResponseEntity<>(jsonUploadMsg.toString(), status); + } + + @RequestMapping(value = "/", method = RequestMethod.POST, produces = { "text/plain" }) + @ResponseStatus(value = HttpStatus.OK) + public ResponseEntity upload(final HttpServletRequest request) throws DataGridConnectionRefusedException { + + logger.info("Uploading files ..."); + String uploadMessage = "File Uploaded. "; + String errorType = ""; + + if (!(request instanceof MultipartHttpServletRequest)) { + logger.debug("Request is not a multipart request."); + uploadMessage = "Request is not a multipart request."; + errorType = FATAL; + return getUploadResponse(uploadMessage, errorType); + } + + MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; + MultipartFile multipartFile = multipartRequest.getFile("file"); + + boolean isRuleDeployment = Boolean.parseBoolean(multipartRequest.getParameter("ruleDeployment")); + boolean checksum = Boolean.parseBoolean(multipartRequest.getParameter("checksum")); + boolean replica = Boolean.parseBoolean(multipartRequest.getParameter("replica")); + boolean overwrite = Boolean.parseBoolean(multipartRequest.getParameter("overwriteDuplicateFiles")); + String resources = multipartRequest.getParameter("resources"); + String resourcesToUpload = multipartRequest.getParameter("resourcesToUpload"); + String destPath = multipartRequest.getParameter("uploadDestinationPath"); + + try { + if (isRuleDeployment) { + ruleDeploymentService.deployRule(multipartFile); + } else { + us.upload(multipartFile, destPath, checksum, replica, resources, resourcesToUpload, overwrite); + } + } catch (DataGridReplicateException e) { + uploadMessage += e.getMessage(); + errorType = WARNING; + } catch (DataGridRuleException e) { + uploadMessage += METADATA_EXTRACTION_FAILED_MSG; + errorType = WARNING; + } catch (DataGridException e) { + uploadMessage = e.getMessage(); + errorType = FATAL; + } + + return getUploadResponse(uploadMessage, errorType); + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/UserBookmarkController.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/UserBookmarkController.java new file mode 100755 index 000000000..bc6f29005 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/UserBookmarkController.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.SessionAttributes; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.core.domain.entity.DataGridUserBookmark; +import com.emc.metalnx.services.interfaces.AdminServices; +import com.emc.metalnx.services.interfaces.GroupService; +import com.emc.metalnx.services.interfaces.IRODSServices; +import com.emc.metalnx.services.interfaces.UserBookmarkService; +import com.emc.metalnx.services.interfaces.UserService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +@Controller +@SessionAttributes({ "addBookmarks", "removeBookmarks" }) +@RequestMapping(value = "/userBookmarks") +public class UserBookmarkController { + @Autowired + UserBookmarkService userBookmarkService; + + @Autowired + UserService userService; + + @Autowired + GroupService groupService; + + @Autowired + IRODSServices irodsServices; + + @Autowired + AdminServices adminServices; + + private Set addBookmark = new HashSet(); + private Set removeBookmark = new HashSet(); + + private int totalUserBookmarks; + private int totalUserBookmarksFiltered; + + private static final String REQUEST_OK = "OK"; + private static final String REQUEST_ERROR = "ERROR"; + + public final static Logger logger = LoggerFactory.getLogger(UserBookmarkController.class); + + @RequestMapping(value = "/") + public String listBookmarks(final Model model) { + String loggedUsername = irodsServices.getCurrentUser(); + String loggedUserZoneName = irodsServices.getCurrentUserZone(); + DataGridUser user = userService.findByUsernameAndAdditionalInfo(loggedUsername, loggedUserZoneName); + + List userBookmakrs = user.getBookmarksSorted(); + + model.addAttribute("userBookmarks", userBookmakrs); + model.addAttribute("foundUserBookmarks", !userBookmakrs.isEmpty()); + + return "bookmarks/userBookmarks"; + } + + @RequestMapping(value = "/addBookmark/") + @ResponseStatus(value = HttpStatus.OK) + public void addBookmark(@RequestParam("path") final String path, final Model model) { + + // Remove from the in-memory list before checking database + if (removeBookmark.contains(path)) { + removeBookmark.remove(path); + } else { + addBookmark.add(path); + model.addAttribute("addBookmark", addBookmark); + } + } + + @RequestMapping(value = "/removeBookmark/") + @ResponseStatus(value = HttpStatus.OK) + public void removeBookmark(@RequestParam("path") final String path) { + + // Removing from the in-memory bookmarks list before checking database + if (addBookmark.contains(path)) { + addBookmark.remove(path); + } else { + removeBookmark.add(path); + } + } + + @RequestMapping(value = "/addBookmarkToUser/") + @ResponseBody + public String addBookmarkToGroup(@RequestParam("username") final String username, + @RequestParam("path") final String path) { + String zoneName = irodsServices.getCurrentUserZone(); + DataGridUser user = userService.findByUsernameAndAdditionalInfo(username, zoneName); + + if (user == null) { + return REQUEST_ERROR; + } + + Set toAdd = new HashSet(); + toAdd.add(path); + + return userBookmarkService.updateBookmarks(user, toAdd, null) ? REQUEST_OK : REQUEST_ERROR; + } + + @RequestMapping(value = "/removeBookmarkFromUser/") + @ResponseBody + public String removeBookmarkFromGroup(@RequestParam("username") final String username, + @RequestParam("path") final String path) { + logger.info("Request for removing a {} bookmark from {}", path, username); + + String zoneName = irodsServices.getCurrentUserZone(); + DataGridUser user = userService.findByUsernameAndAdditionalInfo(username, zoneName); + + if (user == null) { + return REQUEST_ERROR; + } + + Set toRemove = new HashSet(); + toRemove.add(path); + + return userBookmarkService.updateBookmarks(user, null, toRemove) ? REQUEST_OK : REQUEST_ERROR; + } + + @RequestMapping(value = "/bookmarksPaginated") + @ResponseBody + public String bookmarksPaginated(final HttpServletRequest request) { + int draw = Integer.parseInt(request.getParameter("draw")); + int start = Integer.parseInt(request.getParameter("start")); + int length = Integer.parseInt(request.getParameter("length")); + String searchString = request.getParameter("search[value]"); + int orderColumn = Integer.parseInt(request.getParameter("order[0][column]")); + String orderDir = request.getParameter("order[0][dir]"); + boolean onlyCollections = Boolean.parseBoolean(request.getParameter("onlyCollections")); + String loggedUsername = irodsServices.getCurrentUser(); + String loggedUserZoneName = irodsServices.getCurrentUserZone(); + DataGridUser user = userService.findByUsernameAndAdditionalInfo(loggedUsername, loggedUserZoneName); + String[] orderBy = { "name", "path", "created_at", "is_collection" }; + + // checking if there is another column to order by + boolean has2ndColumnToOrderBy = request.getParameter("order[1][column]") != null; + + List userBookmarks = null; + if (has2ndColumnToOrderBy) { + List orderByList = new ArrayList(); + List orderDirList = new ArrayList(); + + int firstCol = Integer.parseInt(request.getParameter("order[0][column]")); + int secondCol = Integer.parseInt(request.getParameter("order[1][column]")); + + orderByList.add(orderBy[firstCol]); + orderDirList.add(request.getParameter("order[0][dir]")); + orderByList.add(orderBy[secondCol]); + orderDirList.add(request.getParameter("order[1][dir]")); + + userBookmarks = userBookmarkService.findBookmarksPaginated(user, start, length, searchString, orderByList, + orderDirList, onlyCollections); + } else { + userBookmarks = userBookmarkService.findBookmarksPaginated(user, start, length, searchString, + orderBy[orderColumn], orderDir, onlyCollections); + } + + ObjectMapper mapper = new ObjectMapper(); + Map jsonResponse = new HashMap(); + String jsonString = ""; + if ("".equals(searchString)) { + totalUserBookmarks = user.getBookmarks().size(); + totalUserBookmarksFiltered = user.getBookmarks().size(); + } else { + totalUserBookmarksFiltered = userBookmarks.size(); + } + + jsonResponse.put("draw", String.valueOf(draw)); + jsonResponse.put("recordsTotal", String.valueOf(totalUserBookmarks)); + jsonResponse.put("recordsFiltered", String.valueOf(totalUserBookmarksFiltered)); + jsonResponse.put("data", userBookmarks); + + try { + jsonString = mapper.writeValueAsString(jsonResponse); + } catch (JsonProcessingException e) { + logger.error("Could not parse user bookmarks to json: {}", e.getMessage()); + } + return jsonString; + } + + public void clearBookmarksLists() { + addBookmark.clear(); + removeBookmark.clear(); + } + + /** + * @return the addBookmark + */ + public Set getAddBookmark() { + return addBookmark; + } + + /** + * @return the removeBookmark + */ + public Set getRemoveBookmark() { + return removeBookmark; + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/utils/LoggedUserUtils.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/utils/LoggedUserUtils.java new file mode 100755 index 000000000..17176d7eb --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/controller/utils/LoggedUserUtils.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.controller.utils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +import com.emc.metalnx.core.domain.entity.DataGridUser; +import com.emc.metalnx.services.interfaces.UserService; + +@Service +public class LoggedUserUtils { + + @Autowired + UserService userService; + + @Value("${irods.zoneName}") + private String zoneName; + + public DataGridUser getLoggedDataGridUser() { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + String username = (String) auth.getPrincipal(); + + return userService.findByUsernameAndAdditionalInfo(username, zoneName); + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/interceptors/HttpResponseHandlerInterceptor.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/interceptors/HttpResponseHandlerInterceptor.java new file mode 100755 index 000000000..fce8501bf --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/interceptors/HttpResponseHandlerInterceptor.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.interceptors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.irods.jargon.core.pub.IRODSAccessObjectFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import com.emc.metalnx.modelattribute.enums.URLMap; +import com.emc.metalnx.services.auth.UserTokenDetails; +import com.emc.metalnx.utils.EmcMetalnxVersion; + +/** + * Class that will intercept HTTP responses to clients. Metalnx will use it to + * close sessions in the grid and add objects pertinent to every response. + */ +public class HttpResponseHandlerInterceptor extends HandlerInterceptorAdapter { + + @Autowired + private IRODSAccessObjectFactory irodsAccessObjectFactory; + + private UserTokenDetails userTokenDetails; + private URLMap urlMap; + private EmcMetalnxVersion emcmetalnxVersion; + + @Override + public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler, + final ModelAndView modelAndView) throws Exception { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + if (modelAndView != null && auth != null) { + if (urlMap == null) { + urlMap = new URLMap(); + } + if (emcmetalnxVersion == null) { + emcmetalnxVersion = new EmcMetalnxVersion(); + } + + modelAndView.getModelMap().addAttribute("urlMap", urlMap); + modelAndView.getModelMap().addAttribute("emcmetalnxVersion", emcmetalnxVersion); + + if (auth instanceof UsernamePasswordAuthenticationToken) { + userTokenDetails = (UserTokenDetails) auth.getDetails(); + modelAndView.getModelMap().addAttribute("userDetails", userTokenDetails.getUser()); + } + } + + // closing sessions to avoid idle agents + irodsAccessObjectFactory.closeSessionAndEatExceptions(); + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/breadcrumb/DataGridBreadcrumb.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/breadcrumb/DataGridBreadcrumb.java new file mode 100755 index 000000000..2e47176f1 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/breadcrumb/DataGridBreadcrumb.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.modelattribute.breadcrumb; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Responsible for managing path items on the colelctions breadcrumb + */ +public class DataGridBreadcrumb { + + private List items; + + public static final String PATH_SEPARATOR = "/"; + + public DataGridBreadcrumb(final String path) { + items = new ArrayList<>(); + + if (PATH_SEPARATOR.equals(path)) { + items.add(new DataGridBreadcrumbItem(PATH_SEPARATOR)); + return; + } + + List pathItems = Arrays.asList(path.split(PATH_SEPARATOR)); + pathItems = pathItems.subList(1, pathItems.size()); + + // Create intermediate items for current path + for (int i = 0; i < pathItems.size(); i++) { + items.add(new DataGridBreadcrumbItem(joinAsPath(pathItems.subList(0, i + 1)))); + } + } + + public List getItems() { + return items; + } + + /** + * Auxiliary method for joining strings as a path + * + * @param items + * list of {@link String} + * @return path {@link String} + */ + private String joinAsPath(final List items) { + StringBuilder sb = new StringBuilder(); + for (String item : items) { + sb.append(PATH_SEPARATOR); + sb.append(item); + } + return sb.toString(); + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/breadcrumb/DataGridBreadcrumbItem.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/breadcrumb/DataGridBreadcrumbItem.java new file mode 100755 index 000000000..6dc9433dc --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/breadcrumb/DataGridBreadcrumbItem.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.modelattribute.breadcrumb; + +/** + * Represents a path item on the breadcrumb + */ +public class DataGridBreadcrumbItem { + + private String name; + private String path; + + public DataGridBreadcrumbItem(final String path) { + this.path = path; + + // Getting last item of the path based on the last occurent of PATH_SEPARATOR + name = path.substring(path.lastIndexOf(DataGridBreadcrumb.PATH_SEPARATOR) + 1, path.length()); + } + + public String getPath() { + return path; + } + + public String getName() { + return name; + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/collection/CollectionOrDataObjectForm.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/collection/CollectionOrDataObjectForm.java new file mode 100755 index 000000000..3aaea4327 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/collection/CollectionOrDataObjectForm.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.modelattribute.collection; + +/** + * Class that represents a form that contains information about a collection or + * data object that is about to change. For collections it contains the name and + * the inherit option and for data object it only contains name. + */ +public class CollectionOrDataObjectForm { + + private String collectionName; + private String path; + private String parentPath; + private boolean inheritOption; + private boolean isCollection; + + /** + * @return the collectionName + */ + public String getCollectionName() { + return collectionName; + } + + /** + * @param collectionName + * the collectionName to set + */ + public void setCollectionName(final String collectionName) { + this.collectionName = collectionName.trim(); + } + + /** + * @return the inheritOption + */ + public boolean getInheritOption() { + return inheritOption; + } + + /** + * @param inheritOption + * the inheritOption to set + */ + public void setInheritOption(final boolean inheritOption) { + this.inheritOption = inheritOption; + } + + /** + * @return the path + */ + public String getPath() { + return path; + } + + /** + * @param path + * the path to set + */ + public void setPath(final String path) { + this.path = path; + } + + /** + * @return the parentPath + */ + public String getParentPath() { + return parentPath; + } + + /** + * @param parentPath + * the parentPath to set + */ + public void setParentPath(final String parentPath) { + this.parentPath = parentPath; + } + + /** + * @return the isCollection + */ + public boolean isCollection() { + return isCollection; + } + + /** + * @param isCollection + * the isCollection to set + */ + public void setCollection(final boolean isCollection) { + this.isCollection = isCollection; + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/enums/ExceptionEnum.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/enums/ExceptionEnum.java new file mode 100755 index 000000000..e7c76890f --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/enums/ExceptionEnum.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.modelattribute.enums; + +import java.util.ArrayList; +import java.util.List; + +/** + * Maps all exceptions to an ID + * + * @author guerra + * + */ + +public enum ExceptionEnum { + + JARGON_EXCEPTION(1), USERS_DATA_DUPLICATE_EXCEPTION(2); + + private int code; + + ExceptionEnum(final int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public static List getExceptionTypeList() { + + List exceptionTypes = new ArrayList(); + for (ExceptionEnum exceptionEnum : ExceptionEnum.values()) { + exceptionTypes.add(exceptionEnum.code); + } + return exceptionTypes; + } + + public static ExceptionEnum findTypeByString(final int exceptionType) { + ExceptionEnum exceptionTypeEnumValue = null; + for (ExceptionEnum exceptionTypeEnum : ExceptionEnum.values()) { + if (exceptionTypeEnum.getCode() == exceptionType) { + exceptionTypeEnumValue = exceptionTypeEnum; + break; + } + } + if (exceptionTypeEnumValue == null) { + exceptionTypeEnumValue = ExceptionEnum.JARGON_EXCEPTION; + } + return exceptionTypeEnumValue; + + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/enums/MetadataTemplateAccessType.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/enums/MetadataTemplateAccessType.java new file mode 100755 index 000000000..1be41095a --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/enums/MetadataTemplateAccessType.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.modelattribute.enums; + +public enum MetadataTemplateAccessType { + + SYSTEM("system"), PRIVATE("private"); + + private String accessType; + + MetadataTemplateAccessType(final String accessType) { + this.accessType = accessType; + } + + /** + * Returns the String access type corresponding to this value + * + * @return + */ + public String getAccessType() { + return accessType; + } + + /** + * Finds the correct label associated to the input String + * + * @param accessType + * @return + */ + public static MetadataTemplateAccessType findByString(final String accessType) { + + if (accessType == null) { + return null; + } + + for (MetadataTemplateAccessType at : MetadataTemplateAccessType.values()) { + if (at.getAccessType().compareTo(accessType) == 0) { + return at; + } + } + return null; + } + + /** + * Returns the string format for this enum + */ + @Override + public String toString() { + return getAccessType(); + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/enums/URLMap.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/enums/URLMap.java new file mode 100755 index 000000000..4320eaf82 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/enums/URLMap.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.modelattribute.enums; + +public class URLMap { + + public static final String URL_DASHBOARD = "/dashboard/"; + + public static final String URL_RULES = "/rules/"; + + public static final String URL_USERS_MANAGEMENT = "/users/"; + public static final String URL_ADD_USER = "add/"; + public static final String URL_MODIFY_USER = "modify/"; + public static final String URL_DELETE_USER = "delete/"; + public static final String URL_FIND_USER = "/users/find/"; + public static final String URL_FIND_ALL_USER = "/users/findAll/"; + public static final String URL_USERS_CSV_REPORT = "/users/usersToCSVFile/"; + public static final String URL_USER_BOOKMARKS = "/userBookmarks/"; + public static final String URL_STARRED_ITEMS = "/favorites/"; + + public static final String URL_USER_PROFILE_MANAGEMENT = "/users/profile/"; + public static final String URL_ADD_USER_PROFILE = "/users/profile/create/"; + public static final String URL_USER_PROFILE_VALIDATE_PROFILE_NAME = "/emc-metalnx-web/users/profile/isValidProfileName/"; + public static final String URL_USER_PROFILES_CSV_REPORT = "/users/profile/profilesToCSVFile/"; + + public static final String URL_REMOVE_USER_PROFILE = "/emc-metalnx-web/users/profile/remove/"; + public static final String URL_MODIFY_USER_PROFILE = "/emc-metalnx-web/users/profile/modify/"; + public static final String URL_USER_VALIDATE_USERNAME = "/emc-metalnx-web/users/isValidUsername/"; + + public static final String URL_GROUPS_MANAGEMENT = "/groups/"; + public static final String URL_ADD_GROUP = "add/"; + public static final String URL_MODIFY_GROUP = "modify/"; + public static final String URL_DELETE_GROUP = "delete/"; + public static final String URL_GROUP_VALIDATE_GROUPNAME = "/emc-metalnx-web/groups/isValidGroupname/"; + public static final String URL_GROUPS_CSV_REPORT = "/groups/groupsToCSVFile/"; + public static final String URL_GROUPS_BOOKMARKS = "/groupBookmarks/groups/"; + + public static final String URL_COLLECTIONS_MANAGEMENT = "/collections/"; + public static final String URL_ADD_COLLECTION = "/emc-metalnx-web/collections/add/"; + public static final String URL_MODIFY_COLLECTION = "/emc-metalnx-web/fileOperation/modify/"; + public static final String URL_DELETE_COLLECTION = "/emc-metalnx-web/fileOperation/delete/"; + public static final String URL_COLLECTION_VALIDATE_NAME = "/emc-metalnx-web/collections/isValidCollectionName/"; + + public static final String URL_METADATA_SEARCH = "/metadata/"; + + public static final String URL_TEMPLATE_MANAGEMENT = "/templates/"; + public static final String URL_ADD_TEMPLATE = "add/"; + public static final String URL_MODIFY_TEMPLATE = "modify/"; + public static final String URL_DELETE_TEMPLATE = "delete/"; + public static final String URL_DELETE_TEMPLATE_FIELD_FROM_DB = "/emc-metalnx-web/templates/removeFieldFromDB"; + public static final String URL_DELETE_TEMPLATE_FIELD = "/emc-metalnx-web/templates/removeFieldFromTemplate"; + public static final String URL_TEMPLATE_VALIDATE_NAME = "/emc-metalnx-web/templates/isValidTemplateName/"; + public static final String URL_EXPORT_TEMPLATE_XML = "exportTemplatesToXMLFile/"; + + public static final String URL_RESOURCES_MANAGEMENT = "/resources/"; + public static final String URL_ADD_RESOURCE = "/resources/add/"; + public static final String URL_ADD_RESOURCE_ACTION = "/resources/add/action/"; + public static final String URL_MODIFY_RESOURCE = "/resources/modify/"; + public static final String URL_MODIFY_RESOURCE_ACTION = "/resources/modify/action/"; + public static final String URL_DELETE_RESOURCE = "/emc-metalnx-web/resources/delete/"; + public static final String URL_RESOURCES_SERVERS = "/resources/servers/"; + public static final String URL_RESOURCES_MAP = "/resources/map/"; + public static final String URL_RESOURCE_VALIDATE_NAME = "/emc-metalnx-web/resources/isValidResourceName/"; + + public static final String URL_SPECIFIC_QUERIES_MANAGEMENT = "/specificqueries/"; + public static final String URL_ADD_SPECIFIC_QUERY_PAGE = "/specificqueries/add/"; + public static final String URL_ADD_SPECIFIC_QUERY = "/specificqueries/add/action/"; + public static final String URL_MODIFY_SPECIFIC_QUERY_PAGE = "/emc-metalnx-web/specificqueries/modify/"; + public static final String URL_MODIFY_SPECIFIC_QUERY = "/specificqueries/modify/action/"; + public static final String URL_DELETE_SPECIFIC_QUERY = "/emc-metalnx-web/specificqueries/remove/"; + public static final String URL_SPECIFIC_QUERY_VALIDATE = "/emc-metalnx-web/specificqueries/validate/"; + + public static final String URL_HOME_COLLECTION_USER = "/collections/home/"; + public static final String URL_PUBLIC_COLLECTION_USER = "/collections/public/"; + public static final String URL_TRASH_COLLECTION_USER = "/collections/trash/"; + public static final String URL_ADD_COLLECTION_USER = "/emc-metalnx-web/collections/add/"; + public static final String URL_MODIFY_COLLECTION_USER = "/emc-metalnx-web/fileOperation/modify/"; + public static final String URL_DELETE_COLLECTION_USER = "/fileOperation/delete/"; + public static final String URL_COLLECTION_VALIDATE_NAME_USER = "/emc-metalnx-web/collections/isValidCollectionName/"; + public static final String URL_LOGOUT = "/logout/"; + + public static final String URL_TICKETS = "/tickets/"; + public static final String URL_TICKETS_FORM = "/emc-metalnx-web/tickets/ticketForm"; + public static final String URL_TICKETS_DELETE = "/emc-metalnx-web/tickets/"; + public static final String URL_TICKETS_CLIENT = "/ticketclient/"; +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/metadatatemplate/MetadataTemplateForm.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/metadatatemplate/MetadataTemplateForm.java new file mode 100755 index 000000000..0b0d7c4e2 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/metadatatemplate/MetadataTemplateForm.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.modelattribute.metadatatemplate; + +import java.util.List; + +public class MetadataTemplateForm { + private Long id; + private String templateName; + private String description; + private String usageInformation; + private String accessType; + private String owner; + private Integer version; + private List avuPositions; + private List avuAttributes; + private List avuValues; + private List avuUnits; + private List paths; + + public List getPaths() { + return paths; + } + + /** + * @return the id + */ + public Long getId() { + return id; + } + + public void setPaths(final List paths) { + this.paths = paths; + } + + /** + * @param id + * the id to set + */ + public void setId(final Long id) { + this.id = id; + } + + public String getTemplateName() { + return templateName; + } + + public void setTemplateName(final String templateName) { + this.templateName = templateName; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public String getUsageInformation() { + return usageInformation; + } + + public void setUsageInformation(final String usageInformation) { + this.usageInformation = usageInformation; + } + + public List getAvuPositions() { + return avuPositions; + } + + public void setAvuPositions(final List avuPositions) { + this.avuPositions = avuPositions; + } + + public List getAvuValues() { + return avuValues; + } + + public void setAvuValues(final List avuValues) { + this.avuValues = avuValues; + } + + public List getAvuAttributes() { + return avuAttributes; + } + + public void setAvuAttributes(final List avuAttributes) { + this.avuAttributes = avuAttributes; + } + + public List getAvuUnits() { + return avuUnits; + } + + public void setAvuUnits(final List avuUnits) { + this.avuUnits = avuUnits; + } + + public String getAccessType() { + return accessType; + } + + public void setAccessType(final String accessType) { + this.accessType = accessType; + } + + public String getOwner() { + return owner; + } + + public void setOwner(final String owner) { + this.owner = owner; + } + + /** + * @return the version + */ + public Integer getVersion() { + return version; + } + + /** + * @param version + * the version to set + */ + public void setVersion(final Integer version) { + this.version = version; + } +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/preferences/UserPreferences.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/preferences/UserPreferences.java new file mode 100755 index 000000000..ec19dfafb --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/preferences/UserPreferences.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.modelattribute.preferences; + +public class UserPreferences { + + private String localeLanguage; + private boolean forceFileOverwriting; + + /** + * Returns whether forceFileOverwriting is set or not + * + * @return + */ + public boolean isForceFileOverwriting() { + return forceFileOverwriting; + } + + /** + * Sets forceFileOverwriting + * + * @param forceFileOverwriting + */ + public void setForceFileOverwriting(final boolean forceFileOverwriting) { + this.forceFileOverwriting = forceFileOverwriting; + } + + /** + * @return the localeLanguage + */ + public String getLocaleLanguage() { + return localeLanguage; + } + + /** + * @param localeLanguage + * the localeLanguage to set + */ + public void setLocaleLanguage(final String localeLanguage) { + this.localeLanguage = localeLanguage; + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/template/field/TemplateFieldForm.java b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/template/field/TemplateFieldForm.java new file mode 100755 index 000000000..e12fea1e7 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/modelattribute/template/field/TemplateFieldForm.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015-2017, Dell EMC + * + * Licensed 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 com.emc.metalnx.modelattribute.template.field; + +public class TemplateFieldForm { + + private Long id; + private String templateName; + private String attribute; + private String attributeValue; + private String attributeUnit; + private float startRange; + private float endRange; + private int order; + private Integer formListPosition; + + /** + * @return the attribute + */ + public String getAttribute() { + return attribute; + } + + /** + * @param attribute + * the attribute to set + */ + public void setAttribute(final String attribute) { + this.attribute = attribute; + } + + /** + * @return the attributeValue + */ + public String getValue() { + return attributeValue; + } + + /** + * @param attributeValue + * the attributeValue to set + */ + public void setValue(final String attributeValue) { + this.attributeValue = attributeValue; + } + + /** + * @return the attributeUnit + */ + public String getUnit() { + return attributeUnit; + } + + /** + * @param attributeUnit + * the attributeUnit to set + */ + public void setUnit(final String attributeUnit) { + this.attributeUnit = attributeUnit; + } + + /** + * @return the startRange + */ + public float getStartRange() { + return startRange; + } + + /** + * @param startRange + * the startRange to set + */ + public void setStartRange(final float startRange) { + this.startRange = startRange; + } + + /** + * @return the endRange + */ + public float getEndRange() { + return endRange; + } + + /** + * @param endRange + * the endRange to set + */ + public void setEndRange(final float endRange) { + this.endRange = endRange; + } + + /** + * @return the order + */ + public int getOrder() { + return order; + } + + /** + * @param order + * the order to set + */ + public void setOrder(final int order) { + this.order = order; + } + + public String getTemplateName() { + return templateName; + } + + public void setTemplateName(final String templateName) { + this.templateName = templateName; + } + + /** + * @return the formListPosition + */ + public Integer getFormListPosition() { + return formListPosition; + } + + /** + * @param formListPosition + * the formListPosition to set + */ + public void setFormListPosition(final Integer formListPosition) { + this.formListPosition = formListPosition; + } + + /** + * @return the id + */ + public Long getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(final Long id) { + this.id = id; + } + + @Override + public boolean equals(final Object obj) { + if (obj instanceof TemplateFieldForm) { + TemplateFieldForm templateFieldForm = (TemplateFieldForm) obj; + // checking if the ID is set + if (getId() != null && templateFieldForm.getId() != null) { + return getId().equals(templateFieldForm.getId()); + } else if (getAttribute() != null && getValue() != null && getUnit() != null) { + return getAttribute().equals(templateFieldForm.getAttribute()) + && getValue().equals(templateFieldForm.getValue()) + && getUnit().equals(templateFieldForm.getUnit()); + } + } + + return false; + } + +} diff --git a/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/utils/.gitignore b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/utils/.gitignore new file mode 100755 index 000000000..f2c27bc8a --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/java/com/emc/metalnx/utils/.gitignore @@ -0,0 +1 @@ +EmcMetalnxVersion.java diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/META-INF/emc-metalnx-shared/emc-metalnx-shared-context.xml b/packaging/src/emc-metalnx-shared/src/main/resources/META-INF/emc-metalnx-shared/emc-metalnx-shared-context.xml new file mode 100755 index 000000000..ba7bc4587 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/META-INF/emc-metalnx-shared/emc-metalnx-shared-context.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/i18n/messages_en.properties b/packaging/src/emc-metalnx-shared/src/main/resources/i18n/messages_en.properties new file mode 100644 index 000000000..df0e9e23a --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/i18n/messages_en.properties @@ -0,0 +1,848 @@ +# +# Copyright (c) 2015-2017, Dell EMC +# +# Licensed 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. +# + +# Misc +text.back=Back +text.creation.date=Creation Date +text.contains=Contains +text.file.name=File Name +text.info=Info +text.delete=Delete +text.is.equals=Is (Equals) +text.is.not.equals=Is Not (Not Equals) +text.close=Close +text.modification.date=Modification Date +text.not.contains=Does Not Contain +text.path=Path +text.replica.number=Replica Number +text.resource.name=Resource Name +text.size=Size +text.owner.name=Owner Name + +#Login +login.label=Login +login.username=Username +login.password=Password +login.page.title=EMC Metalnx + +# Dashboard +dashboard.menu.title=Dashboard +dashboard.page.title=Dashboard +dashboard.popover=Check grid status (resources, servers and storage) and quickly access user management functionalities. + +dashboard.summary.table.files.label=Files + +dashboard.panel.summary.label=Summary +dashboard.panel.total.storage.error.title=Please, check if MetaLnx remote monitor is running on all servers. +dashboard.card.add.template=Add template +dashboard.card.add.user=Add user +dashboard.card.add.group=Add group +dashboard.card.add.profile=Add profile +dashboard.panel.mlx.msi.title=Metalnx Microservices +dashboard.card.msi.version.not.supported=Unsupported version found. +dashboard.card.msi.not.installed=Metalnx MSI package not installed. +dashboard.card.msi.view.details=View Details +dashboard.card.msi.all.installed.and.compatible=All installed microservices are compatible with Metalnx. +dashboard.card.msi.issues.found=Issues were found on the server(s) below: +dashboard.card.msi.pkg.not.supported=Version not supported ({0}). +dashboard.card.rmd.no.communication=Unable to communicate with Metalnx Remote Monitor. +dashboard.card.irods.version.label=iRODS Version +dashboard.isilon.no.isilon.device=No Isilon device found +dashboard.isilon.unreachable.server=Isilon server unreachable + +##Server Details +server.details.total.label=Total +server.details.used.label=Used +server.details.free.label=Free +server.details.shared.label=Shared + +server.details.user.mode.label=User Mode +server.details.system.mode.label=System Model +server.details.idle.task.label=Idle Task +server.details.io.waiting.label=I/O Waiting + +# Misc +search.placeholder=Search +loading.label=Loading... +loading.table.label=Loading table... +loading.data.label=Loading data... +loading.list.label=Loading list... +loading.system.health.status=Getting the System Status... +loading.system.irods.servers=Loading all servers from the grid... +loading.system.isilon.servers=Loading isilon servers from the grid... +loading.system.other.servers=Loading other devices from the grid... +loading.total.storage=Loading total grid storage... +loading.system.resource.map=Loading resource map... +loading.msi.package.version=Loading MSI Package Version... +loading.irods.version=Loading iRODS Version... +search.results.not.found.label=We could not find any result matching +done.button.label=Apply +permissions.label=Permissions +move.label=Move +copy.label=Copy +copy.with.metadata.label=Copy with metadata +none.label=None +upload.label=Upload +replicate.label=Replicate +done.label=Done +browse.label=Browse +select.files.upload=Select files... +server.details.not.found=Sorry, Metalnx could not find any information for this server. +404.page.title=404 - Page Not Found +403.page.title=403 - Access Denied +500.page.title=500 - Internal Server Error +server.down.page.title=iCAT Not Responding +toCSV.label=CSV +trash.deletion.confirmation=

Everything underneath {0} will be deleted.

Are you sure you want to permanently delete all items in the trash?

+empty.trash.label=Empty Trash +card.users.title=Users +card.groups.title=Groups +card.profiles.title=Profiles +card.templates.title=Templates +msi.package.installed=Installed +msi.package.not.installed=Not Installed +msi.package.mlx.accordion=Metalnx +msi.package.irods.accordion=iRODS +msi.package.other.accordion=Other +msi.package.not.found=No microservices found. +replicate.modal.title=Replicate File(s) +rules.menu.title=Rules +rules.management.table.name.label=Rule Name + +# Users +users.menu.title=Users +users.not.removed.successfully=could not be deleted. Please, check if the user home directory is empty. + +## Management +users.management.menu.title=Users +users.management.menu.title.tooltip=Manage Users +users.management.page.title=Users +users.management.page.title.popover=Manage iRods users: View, Add, Edit and Remove. Use Profiles to easily associate different users as members the same groups. +users.management.add.button=Add User +users.management.table.first.name.label=First Name +users.management.table.last.name.label=Last Name +users.management.table.username.label=Username +users.management.table.user.type.label=Type +users.management.table.zone.label=Zone +users.management.table.email.label=Email +users.management.table.type.label=Type +users.management.table.action.label=Action +users.management.result.number.found=user(s) found + +## Add User Form +users.add.form.title=New User +users.add.form.title.popover=Define Account, Groups and Permission settings for a new MetaLnx user. +users.add.form.account.settings=Account Settings +users.add.form.personal.settings=Personal Information +users.add.form.username.label=Username +users.add.form.password.label=Password +users.add.form.password.confirm.label=Password Confirmation +users.add.form.zone.label=Zone +users.add.form.role.label=iRODS System Role +users.add.form.organizational.role.label=Organizational Role +users.add.form.profile.label=Profile +users.add.form.first.name.label=First Name +users.add.form.last.name.label=Last Name +users.add.form.email.label=Email address +users.add.form.user.field1.label=Company +users.add.form.user.field2.label=Department +users.add.form.user.field3.label=Title + +users.add.form.username.placeholder=Enter username +users.add.form.password.placeholder=Enter password +users.add.form.password.confirm.placeholder=Re-enter password +users.add.form.zone.placeholder=Enter zone name +users.add.form.first.name.placeholder=Enter first name +users.add.form.last.name.placeholder=Enter last name +users.add.form.email.placeholder=Enter email +users.add.form.user.field1.placeholder=Enter company +users.add.form.user.field2.placeholder=Enter department +users.add.form.user.field3.placeholder=Enter title +users.add.form.organizational.role.researcher=Researcher +users.add.form.organizational.role.admin=System Administrator +users.add.form.organizational.role.curator=Data Curator +users.add.form.organizational.role.chief.researcher=Chief Researcher +users.add.form.organizational.role.manager=Manager +users.add.form.organizational.role.director=Director + + +users.add.available.groups.label=Available Groups +users.add.attach.action.label=Attach +users.add.group.table.group.name.label=Group Name +users.add.group.table.zone.label=Zone +users.add.group.table.results.found.label=group(s) found +users.add.attached.groups.label=Attached Groups +users.add.edit.groups.label=Edit +users.add.group.info.modal.title=Details of +users.add.group.table.details.label=Details +users.add.additional.permissions.label=Additional Permissions +users.add.view.users.in.group.button=View Users + +users.add.form.profile.select.label=Select the profile +users.add.form.profile.select.notavailable.label=No profiles available + +users.modify.form.title=Modify User +users.list.item.edit=Edit +users.list.item.delete=Delete + +forms.save.action.button=Save +forms.cancel.action.button=Cancel + +users.removal.confirmation.label=Do you really want to delete user +users.removal.confirmation.button.label=Delete User + +##Validation +users.validation.name.duplicated=Someone already has that username +users.validation.name.blank=The username is required and cannot be blank +users.validation.additionalinfo=The zone is required and cannot be blank +users.validation.password.blank=The password is required and cannot be blank +users.validation.password.confirmation.blank=The password confirmation is required and cannot be blank +users.validation.passwords.not.match=Passwords do not match +users.validation.password.min.length=The password must have at least 5 characters +users.validation.additionalinfo.blank=The zone is required and cannot be blank + +profiles.validation.name.duplicated=Another profile already has that name +profiles.validation.name.blank=The profile name is required and cannot be blank + +## Profile management +users.profile.management.page.title=Profiles +users.profile.management.page.title.popover=A profile is a list of groups that can be associated to users. Profiles are very handy when different users must be associated to a common set of groups. +users.profile.management.menu.title=Profiles +users.profile.management.menu.title.tooltip=Manage Profiles +users.profile.management.add.button=Add Profile + +users.profiles.add.group.table.results.found.label=user profile(s) found + +users.profile.table.header.name=Profile Name +users.profile.table.header.description=Profile Description +users.profile.table.header.action=Action +users.profile.add.form.settings.label=User Profile Form +users.profile.add.form.name=Profile Name +users.profile.add.form.description=Description +users.profile.add.form.attach.to.group.label=Groups +users.profile.management.result.number.found=group(s) found +users.profile.management.add.form.table.attach.label=Attach + +users.profile.add.form.placeholder.name=Enter the profile name +users.profile.add.form.placeholder.description=Enter the description of the profile + +users.profile.removal.confirmation.label=Are you sure you want to remove the user profile + +users.profile.management.delete.button=Delete +users.profile.management.edit.button=Edit + +# Groups +groups.menu.title=Groups +groups.not.removed.successfully=could not be deleted. Please, check if the group home directory is empty. + +## Management +groups.management.menu.title=Groups +groups.management.page.title=Groups +groups.management.page.title.popover=Use Groups to easily manage users that have access to the same list of collections and files. + +groups.management.add.button=Add Group +groups.add.form.title=Add Group +groups.add.form.settings.label=Group Settings +gruops.add.form.group.name=Group Name +groups.add.form.zone=Zone +groups.add.form.attach.to.group.label=Attach users to this group +groups.add.form.table.select.label=Select +groups.add.attached.users.label=Attached Users +groups.add.show.users.label=Edit +groups.management.table.action.label=Action + +groups.add.form.placeholder.group.name=Enter group name +groups.add.form.placeholder.zone=Enter zone +groups.list.item.edit=Edit +groups.list.item.delete=Delete +groups.removal.confirmation.label=Do you really want to remove + +groups.modify.form.title=Modify Group +group.collections.view.page.title=Shared +group.collections.view.page.subtitle=(by groups) +group.collections.view.page.title.popover=List of collections and files shared by your groups. +group.collections.results.found.label=group(s) found + +##Validation +groups.validation.name.duplicated=Another group already has that group name +groups.validation.name.blank=The group name is required and cannot be blank +groups.validation.additionalinfo.blank=The zone is required and cannot be blank + +# Resources +resource.label=Resource +resources.menu.title=Resources +resources.management.menu.title=Resource Management +resources.management.page.title=Resources +resources.management.page.subtitle.list=(List View) +resources.management.page.subtitle.map=(Map View) +resources.management.page.subtitle.server=(Listed by Server) +resources.table.select.label=Select +resources.table.id.label=ID +resources.table.name.label=Name +resources.table.zone.label=Zone +resources.table.type.label=Type +resources.table.path.label=Path +resources.table.freeSpace.label=Free Space +resources.table.parent.label=Parent +resources.table.status.label=Status +resources.table.host.label=Host +resources.table.createTime.label=Create Time +resources.table.modifyTime.label=Modify Time +resources.management.map.view=Map view +resources.management.list.view=List view +resources.management.server.view=Server view +resources.management.map.view.label=Map +resources.management.list.view.label=List +resources.management.server.view.label=Server +resources.management.add.button=Add Resource +resources.management.list.item.delete=Delete +resources.management.list.item.info=Info + +# Resources tooltips +resources.management.list.view.tooltip=View Resources List +resources.management.server.view.tooltip=View Resources by Server +resources.management.map.view.tooltip=View Resources Map +resources.management.popover= Manage iRods Resources: View, Add and Remove. Quickly switch resources view: List, Map and Listed by Server. + +##Add Resource Form +resources.add.form.title=Add Resource +resources.add.form.settings.label=Resource Settings +resources.add.form.resource.name=Resource Name +resources.add.form.placeholder.resource.name=Enter Resource Name +resources.add.form.resource.type=Type +resources.add.form.resource.zone=Zone +resources.add.form.placeholder.resource.zone=Enter Zone +resources.add.form.resource.host=Host +resources.add.form.isilon.resource.host=Isilon Host +resources.add.form.placeholder.resource.host=Enter Host +resources.add.form.placeholder.resource.host.optional=Enter Host (optional) +resources.add.form.placeholder.isilon.resource.host=Enter Isilon Host +resources.add.form.resource.info=Info +resources.add.form.placeholder.resource.info=Enter Info +resources.add.form.parent.resource=Parent Resource +resources.add.form.children.resource=Children Resources +resources.add.form.children.label=Attached Resources +resources.add.form.available.resources.label=Available Resources +resources.add.form.edit.resources.label=Edit Resources +resources.add.form.resource.path=Path +resources.add.form.placeholder.resource.path=Enter Path +resources.add.form.placeholder.resource.path.optional=Enter Path (optional) +resources.add.form.resource.status=Status +resources.add.form.placeholder.resource.status=Enter Status +resources.add.form.resource.comment=Comment +resources.add.form.placeholder.resource.comment=Enter Comment +resources.add.form.resource.parent=Parent +resources.add.form.isilon.resource.port=Isilon Port +resources.add.form.placeholder.resource.port=Enter Isilon Port +resources.add.form.isilon.resource.user=Isilon User +resources.add.form.placeholder.resource.user=Enter Isilon User + +resources.add.table.results.found.label=resource(s) found +servers.results.found.label=server(s) found +resources.table.action.label=Action + +resources.confirmation.deletion.title=Resource Deletion +resources.confirmation.deletion.label=If you choose to delete a resource, all resources that are its direct children will become direct children of the zone and any other data existing in it will be lost. Do you want to continue? +resources.deletion.not.successful.label=could not be deleted. This resource contains one or more dataObjects in it. + +resources.view.servers.page.title=Resources +resources.view.map.page.title=Resources + +resources.validation.name.duplicated=Another resource already has that name +resources.validation.name.blank=The resource name is required and cannot be blank +resources.validation.host.blank=The host name is required and cannot be blank +resources.validation.resource.path.blank=The resource path is required and cannot be blank +resources.validation.isilon.host.blank=The isilon host name is required and cannot be blank +resources.validation.isilon.port.blank=The isilon port is required and cannot be blank +resources.validation.isilon.user.blank=The isilon user is required and cannot be blank + +# Zones and federations +zones.menu.title=Zones and Federations + +# iCAT +icat.menu.title=iCAT + +# Private Library +private.menu.title=Private Library + +# Template labels +user.profile.label=User Profile +user.preferences.label=Preferences +user.logout.label=Logout + +# Preferences Page +preferences.page.title=Preferences +preferences.panel.title=System Settings +preferences.language.label=Language +preferences.save.button.label=Save + +#Collections +collections.menu.title=Collections + +##Management +collections.management.menu.title=Collection Management +collections.management.page.title=Collections + +collections.management.add.button=Add Collection + +collections.management.table.collection.select.label=Select +collections.management.table.collection.name.label=Name +collections.management.table.owner.label=Owner +collections.management.table.modified.label=Modified +collections.management.table.created.label=Created +collections.management.table.size.label=Size +collections.management.table.kind.label=File Type +collections.management.table.action.label=Action +collections.management.table.permissions.label=Permissions +collections.management.table.inheritance.label=Inheritance +collections.management.table.inherited.label=[Inherited] +collections.management.table.collection.replicanumber.label=Replica # +collections.management.result.number.found=result(s) found +collections.management.table.kind.collection=collection +collections.management.table.kind.dataobject=file +collections.management.table.replica.number.label=Replica No. +collections.management.table.number.of.copies.label=No. Copies +collections.management.table.collection.path.label=Path +collections.management.table.matches.label=Matches +collections.management.checksum.label=Checksum +collections.management.replicas.label=Replicas +collections.management.table.bookmark.label=Shared Link +collections.management.table.recursive.label=Recursive +collections.management.table.apply.recursion.label=Apply to subcollections and files +collections.management.shared.links.list.by.user=Shared by users +collections.management.shared.links.list.by.group=Shared by groups + +collections.management.table.inheritance.tooltip=If this option is enabled, all of its subcollections will inherit the permissions of the parent collection. The inheritance option can be changed on Collections Management. +collections.management.table.permission.tooltip=Permission can have one of the four values: none, read, write and ownership. If [Inheritance] is displayed beside the permission then inheritance option is enabled for that collection and all its sub-collections will inherit the permissions of the parent collection. The inheritance option can be changed on Collections Management. +collections.management.table.checksum.label=Checksum +collections.management.table.details.label=Details +collections.management.table.path.label=Path +collections.management.table.resource.label=Resource + +collections.management.add.metadata.modal.empty.attribute=The attribute field is required and cannot be blank +collections.management.add.metadata.modal.empty.value=The value field is required and cannot be blank + +collections.management.progress.label.apply.template=Applying templates... +collections.management.progress.label.download=Preparing files for download... +collections.management.progress.label.permissions=Retrieving permissions... +collections.management.btn.action.add.collection=Add Collection +collections.management.btn.action.upload=Upload +collections.management.btn.action.download=Download + +collections.management.msg.access.denied=You do not have permissions to access information in the target collection or for the selected object. + +## Collections tooltips +collections.favorite.button.tooltip=Mark as Favorite +collections.favorite.unmark.button.tooltip=Unmark Favorite +collections.upload.button.tooltip=Upload file(s) to iRods +collections.upload.button.label=Upload +collections.add.button.tooltip=Add Collection +collections.add.button.label=Add Collection +collections.empty.trash.button.tooltip=Remove All Files from Trash +collections.empty.trash.status=Removing... +collections.history.list.header=Recently Visited +collection.title.popover=Browse, manage, create collections and upload files to iRods. Use metadata to organize and easily find data stored iRods. + +##Add collection form +collections.add.form.title=Add Collection +collections.add.form.collection.settings=Collection Settings +collections.add.form.collectionname.label=Collection Name +collections.add.form.dataobjectname.label=Data Object Name +collections.add.form.collectionname.placeholder=Enter collection name +collections.add.form.collectionname.checkbox.inherit=Apply iRods inheritance +collections.add.form.current.path=Current Path +collections.add.form.current.path.alternate=Create collection in +collections.add.form.dataobject.placeholder=Data Object + +## Collections replica modal +collections.replica.delete=Delete Replica +collections.replica.delete.confirm=Are you really sure you want to delete this replica? +collections.replica.delete.success=Replica successfully deleted. +collections.replica.delete.failure=Replica delete failed. + +collections.error.no.permissions=You do not have permissions to access information in the target collection or for the selected object. + + +collections.form.no.access.error=You do not have rights to create collection {0} + +## Collection modification form +collections.edit.form.title=Modify Collection + +## Data Object modification form +dataobject.add.form.title=Modify Data Object +dataobject.add.form.collectionname.label=File name + +##Validation +collections.validation.name.blank=The collection name is required and cannot be blank +collections.validation.name.duplicated=Another collection already has that name +dataobject.validation.name.duplicated=Another file already has that name +dataobject.validation.name.blank=The file name is required and connot be blank + +collections.error.info.tab=An error occured. Please contact your administrator. + +collections.warning.noresources=There is no other resource available for replication. Check if this object is already replicated across all the resources on this zone. + +# Metadata +metadata.menu.title=Metadata +metadata.search.page.title=Search +metadata.search.page.title.popover=Search for files and collections by metadata and file properties. +metadata.search.prompt=Search for files and collections that match one or more of the conditions below: +metadata.search.menu.title=Search +metadata.template.management.menu.title=Templates +metadata.template.management.page.title=Templates +metadata.template.management.page.title.popover=A Template is a list of metadata that can be associated to one or more objects (collections of files). It can be set as public to allow other users or private to keep it visible only to its owner. +metadata.template.table.name.title=Name +metadata.template.table.description.title=Description +metadata.template.management.result.number.found=template(s) found +metadata.template.avu.result.number.found=attribute(s) found +metadata.search.add.criteria=Criteria +no.metadata.available=No metadata available +metadata.modal.add.title=Add Metadata +metadata.modal.add.label.attribute=Attribute +metadata.modal.add.label.value=Value +metadata.modal.add.label.unit=Unit +metadata.modal.add.btn.cancel=Cancel +metadata.modal.add.btn.save=Save changes +metadata.modal.delete.title=Delete Metadata +metadata.modal.delete.msg.info=Delete operation cannot be undone. Once a Metadata is deleted, it cannot be recovered. +metadata.modal.delete.msg.confirm=Are you really sure you want to perform this operation? +metadata.modal.delete.btn.cancel=Cancel +metadata.modal.delete.btn.delete=Delete +metadata.modal.feedback.msg.duplicated=Metadata already exists. +metadata.modal.feedback.msg.added=Metadata successfully added. +metadata.modal.feedback.msg.edit.failed=Metadata editing failed. +metadata.modal.feedback.msg.edited=Metadata successfully edited. +metadata.modal.feedback.msg.remove.failed=Metadata could not be removed. +metadata.modal.feedback.msg.removed=Metadata successfully removed. +metadata.tab.title=Metadata +metadata.tab.btn.add=Metadata +metadata.tab.btn.delete=Delete selected +metadata.tab.btn.csv=CSV +metadata.table.header.attribute=Attribute +metadata.table.header.value=Value +metadata.table.header.unit=Unit +metadata.table.header.actions=Actions +metadata.table.button.label.save=Save +metadata.table.button.label.cancel=Cancel + + +# Template +templates.management.table.title=Metadata +templates.management.table.name.label=Template +templates.management.table.owner.label=Owner +templates.management.table.version.label=Version +templates.management.table.description.label=Description +templates.management.table.usageInformation.label=Usage Information +templates.management.table.accessType.label=Visibility +templates.management.table.createTs.label=Created +templates.management.table.modifyTs.label=Modified +templates.management.table.action.label=Action +template.metadata.access_type.SYSTEM=Public +template.metadata.access_type.PRIVATE=Private +template.metadata.new.form.add.metadata=Metadata +template.metadata.new.form.delete.metadata=Delete +template.metadata.new.form.edit.metadata=Edit + +#Template Fields +template.fields.management.table.attribute.label=Attribute +templates.fields.management.table.value.label=Value +templates.fields.management.table.unit.label=Unit +templates.fields.management.table.startRange.label=Start Range +templates.fields.management.table.endRange.label=End Range +templates.fields.management.table.order.label=Order +templates.fields.management.table.required.label=(required) +template.fields.management.result.number.found=metadata field(s) +templates.fields.management.table.template.label=Template + +templates.metadata.access_type.private.label=Only {0} will view/edit/apply this template. +templates.metadata.access_type.system.label=All users will be able to view/apply this template. + +templates.validation.name.duplicated=Another template already has that name +templates.validation.name.blank=Template name is required and cannot be blank +templates.not.added.successfully=Template could not be added. +templates.name.too.long=Template name cannot exceed 100 characters. +templates.duplicated.avus=Duplicated AVUs are not permitted to be saved. +templates.confirmation.removal.label=Do you want to remove the selected templates? +templates.edit.button=Edit + +# Permissions +permissions.page.title=Permissions +permissions.page.add.button=Permissions +permissions.page.group.add.button=Add Group +permissions.page.user.add.button=Add User +permissions.table.group.label=group +permissions.table.group.column=Group +permission.table.permission.column=Permission +permissions.table.user.group.column=User/Group +permissions.table.systemrole.column=iRODS System Role +permissions.modal.group.add.title=Add Permission to Group(s) +permissions.modal.group.path.label=Path +permissions.modal.group.groups.label=Groups +permissions.modal.group.close.label=Close +permissions.modal.group.addpermissions.label=Add permissions +permissions.modal.user.add.title=Add Permission to User(s) +permissions.modal.user.users.label=Users +permissions.operation.status.updated=Updated +permissions.operation.status.added=Added +permissions.operation.status.removed=Removed +permissions.operation.status.error=Error +no.permission.available=No Permission Available +permission.validation.username.nonexistent=Username does not exist +permission.validation.group.nonexistent=Group does not exist + +# Confirmation messages +confirmation.user.removal.title=User Remove +confirmation.user.removal.label=Do you really want to remove the user +confirmation.profile.removal.title=Profile Remove +confirmation.profile.removal.label=Do you really want to remove these templates +confirmation.removal.label=Do you really want to remove +confirmation.removal.title=Confirmation + +confirmation.deletion.title=Deletion Confirmation +confirmation.deletion.label=If you choose to delete these items, they will be completely removed from the iRODS data grid. Do you want to continue? +confirmation.message.yes=Yes +confirmation.message.no=No +confirmation.message.cancel=Cancel +confirmation.add.successfully=was successfully added +confirmation.modify.successfully=successfully modified +confirmation.delete.successfully=successfully deleted +confirmation.cancel.title=Cancel Operation +confirmation.cancel.label=If you choose to cancel this form, you will lose any unsaved data. Do you want to continue +confirmation.message.ok=Ok + +# Success messages +templates.metadata.success.import.label=Ok. +templates.metadata.success.import.message=All the templates have been correctly imported. + +# Warnings on metadata templates +templates.metadata.partial.import.label=Attention! +templates.metadata.partial.import.message=Some of the metadata templates have not been imported due to their duplicate names. + +# Error Messages +messages.error.1=Jargon Exception +messages.error.2=Data Duplicate Exception + +templates.metadata.error.import.label=Error! +templates.metadata.error.import.message=The templates could not be imported. + +tab.title.dashboard=Metalnx - Dashboard +tab.title.groups=Metalnx - Groups +tab.title.users= Metalnx - Users +tab.title.resources=Metalnx - Resources +tab.title.collections=Metalnx - Collections +tab.title.search=Metalnx - Search +tab.title.templates=Metalnx - Templates +tab.title.shared=Metalnx - Shared Links +tab.title.favorites=Metalnx - Favorites +tab.title.profile=Metalnx - Profile + +# Side bar user menu +sidebar.user.mycollections=My Collections +sidebar.user.public=Public +sidebar.user.zone=Zone +sidebar.user.bookmarks=Shared Links +sidebar.user.favorites=Favorites +sidebar.user.recents=Recents +sidebar.user.shared.items=Shared Items +sidebar.user.trash=Trash +sidebar.user.groups=My Groups +sidebar.tickets=Tickets + +# Bookmarks +bookmarks.table.file.name.label=Name +bookmarks.table.file.path.label=Path +bookmarks.table.created.at.label=Created +bookmarks.table.type.label=Type +bookmarks.table.group.label=Group +bookmarks.result.number.found=shared link(s) found +bookmarks.page.subtitle=(by users) +bookmarks.page.title=Shared +bookmarks.page.title.popover=List of Collections and Files shared by or with other users. +bookmarks.for.group.your.groups.label=The item(s) below are shared with you: + +# Shared Items +shareditems.empty.table=There are no shared items + +# My Groups +mygroups.empty.table=You are not listed in any group + +# Favorites +favorites.page.title=Favorites +favorites.result.number.found=favorite item(s) found +favorites.table.name.label=Name +favorites.table.path.label=Path +favorites.table.owner.label=Owner +favorites.table.created.at.label=Marked on +favorites.table.type.label=Type +favorites.table.permission.label=Permission +favorites.table.unfav.label=Remove +favorites.items.marked.as.favorites=The following item(s) were marked as favorite: +favorites.empty.table=There are no favorites yet + +# Tickets +tab.title.tickets=Tickets +ticket.page.title=Tickets +tickets.title.popover=List of tickets available in iRODS. +ticket.table.string.label=Ticket String +ticket.table.permission.label=Permission +ticket.table.owner.label=Owner +ticket.table.type.label=Type +ticket.delete.confirm.modal.title=Confirm Deletion +ticket.delete.confirm.modal.msg=Delete this ticket? This action cannot be undone. +ticket.delete.confirm.modal.button.cancel=Cancel +ticket.delete.confirm.modal.button.delete=Delete this Ticket +ticket.delete.feedback.success=The ticket was deleted successfully. +ticket.delete.feedback.error=The ticket was not deleted. +ticket.form.path.label=Path +ticket.form.permission.label=Permission +ticket.form.permission.option.read.label=READ +ticket.form.permission.option.write.label=WRITE +ticket.form.ticket.string.label=Ticket String +ticket.form.ticket.path.label=Ticket Path +ticket.form.ticket.string.placeholder=Set a unique name for this ticket +ticket.form.uses.limit.label=Max Uses +ticket.form.uses.limit.hint=This value limits the number of objects that can be uploaded or downloaded via the ticket. For a collection, ensure this value is greater than or equal to the number of items in the collection. +ticket.form.uses.limit.placeholder=Set a limit for uses or this ticket +ticket.form.write.byte.limit.label=Max number of bytes +ticket.form.write.byte.limit.placeholder=Limit the number of bytes that can be written with this ticket +ticket.form.write.file.limit.label=Max number of updates +ticket.form.write.file.limit.placeholder=Set a limit for writing on a file +ticket.form.users.label=Users +ticket.form.users.placeholder=Inform one or more usernames +ticket.form.groups.label=Groups +ticket.form.groups.placeholder=Inform one or more group names +ticket.form.hostnames.label=Hosts +ticket.form.hostnames.placeholder=Inform an IP address (10.26.103.36) +ticket.form.hostnames.add.btn=Add Host +ticket.form.show.advanced.settings.button=Advanced Settings +ticket.form.hide.advanced.settings.button=Hide Advanced Settings +ticket.form.button.cancel=Cancel +ticket.form.button.create=Create Ticket +ticket.form.button.modify=Modify Ticket +ticket.form.title.create=Create Ticket +ticket.form.title.modify=Modify Ticket +ticket.feedback.message.creation.success=New Ticket Created - +ticket.feedback.message.creation.success.copy=Copy Ticket +ticket.feedback.message.creation.success.email=E-mail Ticket +ticket.feedback.message.creation.failure=Ticket not created. +ticket.feedback.message.modify.success=Ticket successfully modified. +ticket.feedback.message.delete.all.success=Tickets successfully deleted. +ticket.feedback.message.modify.failure=Ticket not modified. +ticket.form.expiration.date.label=Expiration Date +ticket.form.expiration.date.placeholder=Set the date this ticket will be invalid +ticket.form.validation.input.uses.limit.not.number=This field must be an integer. +ticket.form.validation.input.write.byte.limit.not.number=This field must be an integer. +ticket.form.validation.input.write.file.limit.not.number=This field must be an integer. +ticket.form.validation.input.ticket.string.not.valid=This field can contain only 'A-Z', 'a-z', '0-9' characters. It cannot contain only numbers. +ticket.share.button.email=Email Ticket +ticket.share.button.copy=Copy Ticket +ticket.share.title=Share Ticket +ticket.share.button.copy.title=Copy to clipboard +ticket.download.error.message=Sorry, item could not be downloaded. +ticket.login.access=I have a ticket +tickets.delete.selected.label=Delete selected ticket(s) +tickets.deletion.confirmation=Are you sure you want to permanently delete the selected ticket(s) from the system?

+ticket.form.host.already.exists=This IP is already listed. +ticket.access.label=Ticket Access +ticket.use.label=Use Ticket + +# Rules + +tab.title.rules=Rules +rules.page.title=Rules +rules.deletion.confirmation=Are you sure you want to permanently delete this rule? It will be removed from all resources? +rules.upload.label=Upload Rule File +rules.menu.title=Rules +rules.table.string.label=Rule Name +rules.title.popover=List of rules deployed on this grid. + +# collection management +collection.management.upload.status=Your upload may be still running in the previous tab and not all files will be available until the upload is complete. +#Empty trash +empty.trash.success=All trash items were removed successfully +empty.trash.failure=Trash items could not be deleted. + +#Empty Template +empty.tamplate.success=Templates were successfully applied. +empty.tamplate.failure=Templates could not be applied. Please, check if there is any duplicated metadata. + +#Collection Creation fail +collection.creation.fail=Add collection failed. Please, check if you have permissions to perform such operation. + +#Items Replicated +items.all.replicated.success=All items replicated successfully. +items.some.replicated.failure=The following items failed to be replicated: +items.some.replicated.failure.permission=. Please, check if you have permissions to perform such operation. + +#Collection Browser +collection.browser.download.fail=Download failed. Please, check if you have permissions to perform such operation. +collection.browser.upload.fail=Upload failed. Please, check if you have permissions to perform such operation. +collection.browser.upload.success=The selected files were successfully uploaded. +collection.browser.item.not.moved=The following items failed to be moved: +collection.browser.item.not.copied=The following items failed to be copied: +collection.browser.files.deleted.success=The selected files were successfully deleted. +collection.browser.files.deleted.fail=The following items failed to be deleted: + +#listTemplateFieldsForCollections +template.field.list.not.found=No attributes found. + +#favorites +favorite.page.content.path.not.remove=Could not remove path from favorites. + +#httperrors +httperror.403.message=Sorry, you do not have permissions to access that page. Your session is no longer valid. +httperror.404.message=Sorry, we could not process your request. +httperror.500.message=Sorry, something went wrong. Please, contact your system administrator. +httperror.server.no.response.message=No server response + +#login +login.invalid.username.password=Invalid username or password +login.irods.no.response=iRODS not responding +login.databse.no.response=Database not responding +login.invalid.ticket=Invalid ticket string or path +login.copyright=© 2015-2017 DELL EMC All rights reserved. + +#permissions +#It has title and text - test and update if needed + +#prefrences +refrences.index.overwrite.duplicates=Overwrite duplicate files by default + +#template +template.management.not.modified=Sorry. The template could not be modified. + +#ticket in service & template in admin +about.modal= This software contains the intellectual property of EMC + Corporation or is licensed to DELL EMC Corporation from third parties. + Use of this software and the intellectual property contained + therein is expressly limited to the terms and conditions of the + License Agreement under which it is provided by or on behalf of + EMC. + +#Metalnx admin + +#collection + +collections.group.form.permission=Do you want to apply this change to subcollections and files in +collections.user.form.permissions=Do you want to apply this change to subcollections and files in + +#dashboard +#isilonserver +dashboard.isilon.unreachable.server=Unreachable Isilon Server(s). +dashboard.isilon.no.isilon.device=There are no Isilon devices to be displayed. + +#nonResourceServer +dashboard.nonresourceserver.other.devices=There are no other devices to be displayed. diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/bootstrap-tagsinput.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/bootstrap-tagsinput.css new file mode 100644 index 000000000..55f7c09df --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/bootstrap-tagsinput.css @@ -0,0 +1,46 @@ +.bootstrap-tagsinput { + background-color: #fff; + border: 1px solid #ccc; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + display: inline-block; + padding: 4px 6px; + margin-bottom: 10px; + color: #555; + vertical-align: middle; + border-radius: 4px; + max-width: 100%; + line-height: 22px; + cursor: text; +} +.bootstrap-tagsinput input { + border: none; + box-shadow: none; + outline: none; + background-color: transparent; + padding: 0; + margin: 0; + width: auto !important; + max-width: inherit; +} +.bootstrap-tagsinput input:focus { + border: none; + box-shadow: none; +} +.bootstrap-tagsinput .tag { + margin-right: 2px; + color: white; +} +.bootstrap-tagsinput .tag [data-role="remove"] { + margin-left: 8px; + cursor: pointer; +} +.bootstrap-tagsinput .tag [data-role="remove"]:after { + content: "x"; + padding: 0px 2px; +} +.bootstrap-tagsinput .tag [data-role="remove"]:hover { + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} +.bootstrap-tagsinput .tag [data-role="remove"]:hover:active { + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/bootstrap.min.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/bootstrap.min.css new file mode 100644 index 000000000..3a836f468 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/bootstrap.min.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.2.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(/emc-metalnx-web/fonts/glyphicons-halflings-regular.eot);src:url(/emc-metalnx-web/fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(/emc-metalnx-web/fonts/glyphicons-halflings-regular.woff) format('woff'),url(/emc-metalnx-web/fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(/emc-metalnx-web/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;width:100% \9;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;width:100% \9;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px;line-height:1.42857143 \0}input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;min-height:20px;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],input[type=radio].disabled,input[type=checkbox].disabled,fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm,.form-horizontal .form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg,.form-horizontal .form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:25px;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{position:absolute;z-index:-1;filter:alpha(opacity=0);opacity:0}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:left;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#777}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#777}.navbar-inverse .navbar-nav>li>a{color:#777}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#777}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#777}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#428bca;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar[aria-valuenow="1"],.progress-bar[aria-valuenow="2"]{min-width:30px}.progress-bar[aria-valuenow="0"]{min-width:30px;color:#777;background-color:transparent;background-image:none;-webkit-box-shadow:none;box-shadow:none}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate3d(0,-25%,0);-o-transform:translate3d(0,-25%,0);transform:translate3d(0,-25%,0)}.modal.in .modal-dialog{-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-size:12px;line-height:1.4;visibility:visible;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed;-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/bootstrapValidator.min.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/bootstrapValidator.min.css new file mode 100644 index 000000000..72b6f5bff --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/bootstrapValidator.min.css @@ -0,0 +1,11 @@ +/*! + * BootstrapValidator (http://bootstrapvalidator.com) + * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 + * + * @version v0.5.2, built on 2014-09-25 4:01:07 PM + * @author https://twitter.com/nghuuphuoc + * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc + * @license MIT + */ + +.bv-form .help-block{margin-bottom:0}.bv-form .tooltip-inner{text-align:left}.nav-tabs li.bv-tab-success>a{color:#3c763d}.nav-tabs li.bv-tab-error>a{color:#a94442}.bv-form .bv-icon-no-label{top:0}.bv-form .bv-icon-input-group{top:0;z-index:100} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/dashboard.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/dashboard.css new file mode 100644 index 000000000..e0e3632b8 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/dashboard.css @@ -0,0 +1,105 @@ +/* + * Base structure + */ + +/* Move down content because we have a fixed navbar that is 50px tall */ +body { + padding-top: 50px; +} + + +/* + * Global add-ons + */ + +.sub-header { + padding-bottom: 10px; + border-bottom: 1px solid #eee; +} + +/* + * Top navigation + * Hide default border to remove 1px line. + */ +.navbar-fixed-top { + border: 0; +} + +/* + * Sidebar + */ + +/* Hide for mobile, show later */ +.sidebar { + display: none; +} +@media (min-width: 768px) { + .sidebar { + position: fixed; + top: 51px; + bottom: 0; + left: 0; + z-index: 1000; + display: block; + padding: 20px; + overflow-x: hidden; + overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ + background-color: #f5f5f5; + border-right: 1px solid #eee; + } +} + +/* Sidebar navigation */ +.nav-sidebar { + margin-right: -21px; /* 20px padding + 1px border */ + margin-bottom: 20px; + margin-left: -20px; +} +.nav-sidebar > li > a { + padding-right: 20px; + padding-left: 20px; +} +.nav-sidebar > .active > a, +.nav-sidebar > .active > a:hover, +.nav-sidebar > .active > a:focus { + color: #fff; + background-color: #428bca; +} + + +/* + * Main content + */ + +.main { + padding: 20px; +} +@media (min-width: 768px) { + .main { + padding-right: 40px; + padding-left: 40px; + } +} +.main .page-header { + margin-top: 0; +} + + +/* + * Placeholder dashboard ideas + */ + +.placeholders { + margin-bottom: 30px; + text-align: center; +} +.placeholders h4 { + margin-bottom: 0; +} +.placeholder { + margin-bottom: 20px; +} +.placeholder img { + display: inline-block; + border-radius: 50%; +} diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/dashboardStyle.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/dashboardStyle.css new file mode 100644 index 000000000..4ed25785d --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/dashboardStyle.css @@ -0,0 +1,80 @@ +.panel-footer { + background: none; + border-top: none; +} + +/****************************************** CHARTS ************************************************/ +#serverAndSystemHealthSection{ + margin: 0; +} + +#serversListPanel .row{ + margin: 0; + padding: 0; +} + +#diskSection div .arc { + stroke-weight: 0.1; + fill: #1d871b; +} + + +.selectedRadial { + border-radius: 3px; + background: #f4f4f4; + color: #000; + box-shadow: 0 1px 5px rgba(0,0,0,0.4); + -moz-box-shadow: 0 1px 5px rgba(0,0,0,0.4); + border: 1px solid rgba(200,200,200,0.85); +} + +.radial { + border-radius: 3px; + background: #FFFFFF; + color: #000; + +} + +.background { + fill: #FFFFFF; + fill-opacity: 0.01; +} + +.component { + fill: #e1e1e1; +} + +.component .label { + text-anchor: middle; + fill: #333; + color: inherit; + font-weight: normal; +} + +.arc { + stroke-weight:0.1; + fill: #4e8fff; +} + + +.arc2 { + stroke-weight:0.1; + fill: #3660b0; +} + + +.label { + text-anchor: middle; + font-size: inherit; +} +#mlxMsiPkgList .label { + font-size: 75%; +} +#mlxMsiPkgList > ul ul li:hover{ + cursor: default; +} + +.radial-svg { + display: block; + margin: 0 auto; +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/dataTables.bootstrap.min.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/dataTables.bootstrap.min.css new file mode 100644 index 000000000..0fb975776 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/dataTables.bootstrap.min.css @@ -0,0 +1 @@ +table.dataTable{clear:both;margin-top:6px !important;margin-bottom:6px !important;max-width:none !important}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}div.dataTables_wrapper div.dataTables_length label{font-weight:normal;text-align:left;white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:75px;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:normal;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:0.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:8px;white-space:nowrap}div.dataTables_wrapper div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_wrapper div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap}table.dataTable thead>tr>th.sorting_asc,table.dataTable thead>tr>th.sorting_desc,table.dataTable thead>tr>th.sorting,table.dataTable thead>tr>td.sorting_asc,table.dataTable thead>tr>td.sorting_desc,table.dataTable thead>tr>td.sorting{padding-right:30px}table.dataTable thead>tr>th:active,table.dataTable thead>tr>td:active{outline:none}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable thead .sorting:after,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_desc:after,table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{position:absolute;bottom:8px;right:8px;display:block;font-family:'Glyphicons Halflings';opacity:0.5}table.dataTable thead .sorting:after{opacity:0.2;content:"\e150"}table.dataTable thead .sorting_asc:after{content:"\e155"}table.dataTable thead .sorting_desc:after{content:"\e156"}table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{color:#eee}div.dataTables_scrollHead table.dataTable{margin-bottom:0 !important}div.dataTables_scrollBody table{border-top:none;margin-top:0 !important;margin-bottom:0 !important}div.dataTables_scrollBody table thead .sorting:after,div.dataTables_scrollBody table thead .sorting_asc:after,div.dataTables_scrollBody table thead .sorting_desc:after{display:none}div.dataTables_scrollBody table tbody tr:first-child th,div.dataTables_scrollBody table tbody tr:first-child td{border-top:none}div.dataTables_scrollFoot table{margin-top:0 !important;border-top:none}@media screen and (max-width: 767px){div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{text-align:center}}table.dataTable.table-condensed>thead>tr>th{padding-right:20px}table.dataTable.table-condensed .sorting:after,table.dataTable.table-condensed .sorting_asc:after,table.dataTable.table-condensed .sorting_desc:after{top:6px;right:6px}table.table-bordered.dataTable{border-collapse:separate !important}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-left-width:0}table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable td:last-child,table.table-bordered.dataTable td:last-child{border-right-width:0}table.table-bordered.dataTable tbody th,table.table-bordered.dataTable tbody td{border-bottom-width:0}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0} diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/font-awesome.min.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/font-awesome.min.css new file mode 100644 index 000000000..d0603cb4b --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.5.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"} diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_asc.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_asc.png new file mode 100644 index 000000000..e1ba61a80 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_asc.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_asc_disabled.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_asc_disabled.png new file mode 100644 index 000000000..fb11dfe24 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_asc_disabled.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_both.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_both.png new file mode 100644 index 000000000..af5bc7c5a Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_both.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_desc.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_desc.png new file mode 100644 index 000000000..0e156deb5 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_desc.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_desc_disabled.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_desc_disabled.png new file mode 100644 index 000000000..c9fdd8a15 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/sort_desc_disabled.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_0_aaaaaa_40x100.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 000000000..127bc8fad Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_0_aaaaaa_40x100.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_0_eeeeee_40x100.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_0_eeeeee_40x100.png new file mode 100644 index 000000000..a40857582 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_0_eeeeee_40x100.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_55_c0402a_40x100.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_55_c0402a_40x100.png new file mode 100644 index 000000000..f035f13e2 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_55_c0402a_40x100.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_55_eeeeee_40x100.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_55_eeeeee_40x100.png new file mode 100644 index 000000000..a40857582 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_55_eeeeee_40x100.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_75_ffffff_40x100.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 000000000..56d968868 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_flat_75_ffffff_40x100.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_100_f8f8f8_1x400.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_100_f8f8f8_1x400.png new file mode 100644 index 000000000..4d8b63898 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_100_f8f8f8_1x400.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_35_dddddd_1x400.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_35_dddddd_1x400.png new file mode 100644 index 000000000..53213ce19 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_35_dddddd_1x400.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_55_fbf9ee_1x400.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 000000000..f7200484e Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_55_fbf9ee_1x400.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_60_eeeeee_1x400.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_60_eeeeee_1x400.png new file mode 100644 index 000000000..198cd7a58 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_60_eeeeee_1x400.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_65_ffffff_1x400.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 000000000..075d84394 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_65_ffffff_1x400.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_75_dadada_1x400.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 000000000..addcc6b9b Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_75_dadada_1x400.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_75_e6e6e6_1x400.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 000000000..ecad24f1c Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_75_e6e6e6_1x400.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_95_fef1ec_1x400.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 000000000..f6e3c36a4 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_glass_95_fef1ec_1x400.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 000000000..ae4f11983 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_inset-hard_75_999999_1x100.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_inset-hard_75_999999_1x100.png new file mode 100644 index 000000000..cd996f6d1 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_inset-hard_75_999999_1x100.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_inset-soft_50_c9c9c9_1x100.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_inset-soft_50_c9c9c9_1x100.png new file mode 100644 index 000000000..ebaaa2142 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-bg_inset-soft_50_c9c9c9_1x100.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_222222_256x240.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_222222_256x240.png new file mode 100644 index 000000000..e9c8e16ac Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_222222_256x240.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_2e83ff_256x240.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 000000000..f2bf83883 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_2e83ff_256x240.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_3383bb_256x240.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_3383bb_256x240.png new file mode 100644 index 000000000..956671ae8 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_3383bb_256x240.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_454545_256x240.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_454545_256x240.png new file mode 100644 index 000000000..d6169e8bf Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_454545_256x240.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_70b2e1_256x240.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_70b2e1_256x240.png new file mode 100644 index 000000000..12880a413 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_70b2e1_256x240.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_888888_256x240.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_888888_256x240.png new file mode 100644 index 000000000..d3e6e02a0 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_888888_256x240.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_999999_256x240.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_999999_256x240.png new file mode 100644 index 000000000..e6763f112 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_999999_256x240.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_cd0a0a_256x240.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 000000000..493701892 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_cd0a0a_256x240.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_fbc856_256x240.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_fbc856_256x240.png new file mode 100644 index 000000000..40f52838f Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/images/ui-icons_fbc856_256x240.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/jquery-ui-timepicker-addon.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/jquery-ui-timepicker-addon.css new file mode 100644 index 000000000..2d9e03143 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/jquery-ui-timepicker-addon.css @@ -0,0 +1,27 @@ +.ui-timepicker-div .ui-widget-header { margin-bottom: 8px; } +.ui-timepicker-div dl { text-align: left; } +.ui-timepicker-div dl dt { float: left; clear:left; padding: 0 0 0 5px; } +.ui-timepicker-div dl dd { margin: 0 10px 10px 40%; } +.ui-timepicker-div td { font-size: 90%; } +.ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; } +.ui-timepicker-div .ui_tpicker_unit_hide{ display: none; } + +.ui-timepicker-rtl{ direction: rtl; } +.ui-timepicker-rtl dl { text-align: right; padding: 0 5px 0 0; } +.ui-timepicker-rtl dl dt{ float: right; clear: right; } +.ui-timepicker-rtl dl dd { margin: 0 40% 10px 10px; } + +/* Shortened version style */ +.ui-timepicker-div.ui-timepicker-oneLine { padding-right: 2px; } +.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_time, +.ui-timepicker-div.ui-timepicker-oneLine dt { display: none; } +.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_time_label { display: block; padding-top: 2px; } +.ui-timepicker-div.ui-timepicker-oneLine dl { text-align: right; } +.ui-timepicker-div.ui-timepicker-oneLine dl dd, +.ui-timepicker-div.ui-timepicker-oneLine dl dd > div { display:inline-block; margin:0; } +.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_minute:before, +.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_second:before { content:':'; display:inline-block; } +.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_millisec:before, +.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_microsec:before { content:'.'; display:inline-block; } +.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_unit_hide, +.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_unit_hide:before{ display: none; } \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/jquery-ui.min.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/jquery-ui.min.css new file mode 100644 index 000000000..24c777bd4 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/jquery-ui.min.css @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.11.4 - 2015-06-17 +* http://jqueryui.com +* Includes: core.css, draggable.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, menu.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;min-height:0;font-size:100%}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:none}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,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");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none;cursor:pointer}.ui-selectmenu-button span.ui-icon{right:0.5em;left:auto;margin-top:-8px;position:absolute;top:50%}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:0.4em 2.1em 0.4em 1em;display:block;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_888888_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_2e83ff_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cd0a0a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/jquery.dataTables.min.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/jquery.dataTables.min.css new file mode 100644 index 000000000..ddd9e484f --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/jquery.dataTables.min.css @@ -0,0 +1 @@ +table.dataTable{width:100%;margin:0 auto;clear:both;border-collapse:separate;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable thead th,table.dataTable thead td{padding:10px 18px;border-bottom:1px solid #111}table.dataTable thead th:active,table.dataTable thead td:active{outline:none}table.dataTable tfoot th,table.dataTable tfoot td{padding:10px 18px 6px 18px;border-top:1px solid #111}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc{cursor:pointer;*cursor:hand}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{background-repeat:no-repeat;background-position:center right}table.dataTable thead .sorting{background-image:url("images/sort_both.png")}table.dataTable thead .sorting_asc{background-image:url("images/sort_asc.png")}table.dataTable thead .sorting_desc{background-image:url("images/sort_desc.png")}table.dataTable thead .sorting_asc_disabled{background-image:url("images/sort_asc_disabled.png")}table.dataTable thead .sorting_desc_disabled{background-image:url("images/sort_desc_disabled.png")}table.dataTable tbody tr{background-color:#ffffff}table.dataTable tbody tr.selected{background-color:#B0BED9}table.dataTable tbody th,table.dataTable tbody td{padding:8px 10px}table.dataTable.row-border tbody th,table.dataTable.row-border tbody td,table.dataTable.display tbody th,table.dataTable.display tbody td{border-top:1px solid #ddd}table.dataTable.row-border tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.display tbody tr:first-child td{border-top:none}table.dataTable.cell-border tbody th,table.dataTable.cell-border tbody td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr th:first-child,table.dataTable.cell-border tbody tr td:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child th,table.dataTable.cell-border tbody tr:first-child td{border-top:none}table.dataTable.stripe tbody tr.odd,table.dataTable.display tbody tr.odd{background-color:#f9f9f9}table.dataTable.stripe tbody tr.odd.selected,table.dataTable.display tbody tr.odd.selected{background-color:#acbad4}table.dataTable.hover tbody tr:hover,table.dataTable.display tbody tr:hover{background-color:#f6f6f6}table.dataTable.hover tbody tr:hover.selected,table.dataTable.display tbody tr:hover.selected{background-color:#aab7d1}table.dataTable.order-column tbody tr>.sorting_1,table.dataTable.order-column tbody tr>.sorting_2,table.dataTable.order-column tbody tr>.sorting_3,table.dataTable.display tbody tr>.sorting_1,table.dataTable.display tbody tr>.sorting_2,table.dataTable.display tbody tr>.sorting_3{background-color:#fafafa}table.dataTable.order-column tbody tr.selected>.sorting_1,table.dataTable.order-column tbody tr.selected>.sorting_2,table.dataTable.order-column tbody tr.selected>.sorting_3,table.dataTable.display tbody tr.selected>.sorting_1,table.dataTable.display tbody tr.selected>.sorting_2,table.dataTable.display tbody tr.selected>.sorting_3{background-color:#acbad5}table.dataTable.display tbody tr.odd>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd>.sorting_1{background-color:#f1f1f1}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#f3f3f3}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:whitesmoke}table.dataTable.display tbody tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody tr.even>.sorting_1,table.dataTable.order-column.stripe tbody tr.even>.sorting_1{background-color:#fafafa}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#fcfcfc}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fefefe}table.dataTable.display tbody tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{background-color:#eaeaea}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{background-color:#ececec}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{background-color:#efefef}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{background-color:#a5b2cb}table.dataTable.no-footer{border-bottom:1px solid #111}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable.compact thead th,table.dataTable.compact thead td{padding:4px 17px 4px 4px}table.dataTable.compact tfoot th,table.dataTable.compact tfoot td{padding:4px}table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable th,table.dataTable td{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:0.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:0.755em}.dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:0.25em}.dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:0.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;*cursor:hand;color:#333 !important;border:1px solid transparent;border-radius:2px}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#333 !important;border:1px solid #979797;background-color:white;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(100%, #dcdcdc));background:-webkit-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-moz-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-ms-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:-o-linear-gradient(top, #fff 0%, #dcdcdc 100%);background:linear-gradient(to bottom, #fff 0%, #dcdcdc 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#666 !important;border:1px solid transparent;background:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:white !important;border:1px solid #111;background-color:#585858;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));background:-webkit-linear-gradient(top, #585858 0%, #111 100%);background:-moz-linear-gradient(top, #585858 0%, #111 100%);background:-ms-linear-gradient(top, #585858 0%, #111 100%);background:-o-linear-gradient(top, #585858 0%, #111 100%);background:linear-gradient(to bottom, #585858 0%, #111 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:none;background-color:#2b2b2b;background:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));background:-webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:-o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);background:linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);box-shadow:inset 0 0 3px #111}.dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:white;background:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0)));background:-webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:-o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%);background:linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%)}.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_processing,.dataTables_wrapper .dataTables_paginate{color:#333}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollHead table,.dataTables_wrapper.no-footer div.dataTables_scrollBody table{border-bottom:none}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width: 767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:0.5em}}@media screen and (max-width: 640px){.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:0.5em}} diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/nanoscroller.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/nanoscroller.css new file mode 100644 index 000000000..5b2c5229a --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/nanoscroller.css @@ -0,0 +1,55 @@ +/** initial setup **/ +.nano { + position : relative; + width : 100%; + height : 100%; + overflow : hidden; +} +.nano > .nano-content { + position : absolute; + overflow : scroll; + overflow-x : hidden; + top : 0; + right : 0; + bottom : 0; + left : 0; +} +.nano > .nano-content:focus { + outline: thin dotted; +} +.nano > .nano-content::-webkit-scrollbar { + display: none; +} +.has-scrollbar > .nano-content::-webkit-scrollbar { + display: block; +} +.nano > .nano-pane { + background : rgba(0,0,0,.25); + position : absolute; + width : 10px; + right : 0; + top : 0; + bottom : 0; + visibility : hidden\9; /* Target only IE7 and IE8 with this hack */ + opacity : .01; + -webkit-transition : .2s; + -moz-transition : .2s; + -o-transition : .2s; + transition : .2s; + -moz-border-radius : 5px; + -webkit-border-radius : 5px; + border-radius : 5px; +} +.nano > .nano-pane > .nano-slider { + background: #444; + background: rgba(0,0,0,.5); + position : relative; + margin : 0 1px; + -moz-border-radius : 3px; + -webkit-border-radius : 3px; + border-radius : 3px; +} +.nano:hover > .nano-pane, .nano-pane.active, .nano-pane.flashed { + visibility : visible\9; /* Target only IE7 and IE8 with this hack */ + opacity : 0.99; +} diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/styles-login.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/styles-login.css new file mode 100644 index 000000000..41318e1b9 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/styles-login.css @@ -0,0 +1,205 @@ +body{ + height: 100vh; + margin:0; + overflow: hidden; + background: #2c95dd ; /* Old browsers */ + background: -moz-linear-gradient(-45deg, #ffffff 0%, #2c95dd 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, right bottom, color-stop(0%,#ffffff ), color-stop(100%,#2c95dd)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(-45deg, #ffffff 0%,#2c95dd 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(-45deg, #ffffff 0%,#2c95dd 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(-45deg, #ffffff 0%,#2c95dd 100%); /* IE10+ */ + background: linear-gradient(135deg, #ffffff 0%,#2c95dd 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff ', endColorstr='#2c95dd',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ + ::-webkit-input-placeholder { + color: #4E546D + } +} + +p{ + color: #5B5E6F; + font-size:10px; + text-align:left; +} + +.login{ + font-size: 14px; + opacity:1; + top:15px; + -webkit-transition-timing-function: cubic-bezier(0.68, -0.25, 0.265, .85); + transition-property:transform,opacity,box-shadow,top,left; + transition-duration:.5s; + transform-origin:161px 100%; + transform:rotateX(0deg); + position:relative; + width:360px; + border-top: 2px solid #2c95dd; + height:450px; + position:absolute; + left:0; + right:0; + margin:auto; + top:0; + bottom:0; + padding:100px 40px 40px 40px; + background: #35394a; /* Old browsers */ + background: -moz-linear-gradient(45deg, #35394a 0%, #1f222e 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left bottom, right top, color-stop(0%,#35394a), color-stop(100%,#1f222e)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* IE10+ */ + background: linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#35394a', endColorstr='#1f222e',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ +} + +.login_title { + color:rgb(175, 177, 190); + text-align:left; + font-size:20px; + padding-bottom: 40px; +} + +.login_fields { + height: 208px; + position: absolute; + left: 0; + right: 0; +} + +input[type='password'] { + color:#2c95dd !important; +} + +input[type="submit"] { + border-radius:50px; +} + +input[type="submit"]:hover { + color:white; + background:#2c95dd ; + cursor:pointer; + transition-property:background,color; + transition-duration:.2s; +} + +input[type="submit"]:focus{ + box-shadow:none; + outline:none; +} + +input[type='text'],input[type='password']{ + color: #2c95dd; + width: 100%; + margin-top:-2px; + background: rgb(50, 54, 74); + left: 0; + right: 0; + padding: 10px 40px; + border-top: 2px solid rgb(57, 61, 82); + border-bottom: 2px solid rgb(57, 61, 82); + border-right: none; + border-left: none; + outline: none; + box-shadow: none; + font-size: 16px; + font-family: verdana, sans-serif; + padding-left: 55px; +} + +.login_fields_user, .login_fields_password{ + position:relative; +} + +.login_fields_submit{ + position: relative; + top: 35px; + left: 0; + right: 0; + margin: auto; + padding-left: 40px; +} + +.login_fields_submit input { + padding: 10px 50px; + font-size: 16px; +} + +input{ + background:transparent; + font-family: verdana, sans-serif; + padding:10px 50px; + border:2px solid #2c95dd ; + color: #ffffff ; + transition-property:background,color; + transition-duration:.2s; +} + +.login_msgs { + bottom: 40px; + color: #c0605e; +} + +.login_msgs, .login_copyright { + position: absolute; + left: 0; + right: 0; + padding: 10px 40px; +} + +.login_copyright { + bottom: 0; +} + +.login_copyright span { + font-size: 10px; + color: #636b8b; +} + +.icon{ + position: absolute; + z-index: 1; + left: 36px; + top: 15px; + opacity:1; + color: #a4a6b5; + font-size: 16px; +} + +.icon-input-check { + position: absolute; + z-index: 1; + right: 15px; + top: 15px; + opacity:1; + color: #2c95dd; + font-size: 16px; + } + +img { + width: 85%; +} + +::-webkit-input-placeholder { /* Chrome/Opera/Safari */ + color: #a4a6b5; +} +::-moz-placeholder { /* Firefox 19+ */ + color: #a4a6b5; +} +:-ms-input-placeholder { /* IE 10+ */ + color: #a4a6b5; +} +:-moz-placeholder { /* Firefox 18- */ + color: #a4a6b5; +} + +#haveTicketBtn, #login-link { + margin-left: 50px; + font-size: 16px; +} + +#haveTicketBtn { + margin-left: 50px; +} + +#login-link { + margin-left: 99px; +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/css/styles.css b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/styles.css new file mode 100644 index 000000000..3f1d8cf2e --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/css/styles.css @@ -0,0 +1,2321 @@ +* { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + + +@font-face { + font-family: websiteFont; + src: url("../fonts/Lato-Regular.ttf"); +} + +html{ + overflow: auto; +} + +body { + background-color: #f8f8f8; + font-family: websiteFont; + color: #656363; +} + +#page-wrapper-tickets .panel.loading p { + clear: both; +} + +#page-wrapper-tickets .property-title { + margin-left: 0; +} + +.btn.disabled, .btn[disabled], fieldset[disabled] .btn{ + opacity: 1 !important; + border-color: lightgray; + color:lightgray; +} + +.btn.disabled > i, .btn[disabled] >i , fieldset[disabled] .btn >i{ + opacity: 0.45 !important; +} + +#uploadStatusIcon .fa.fa-tasks.white-icon{ + font-size:18px; +} + +#uploadStatusIcon .badge{ + left: -10px; + top: -8px; + font-size: 10px; + padding: 3px 3px; + position: relative; +} + +#uploadStatusIcon .badge.danger { + background-color: #ce4844; +} + +#uploadStatusIcon .badge.warning { + background-color: #e59307; +} + +#uploadStatusIcon .badge.success { + background-color: #429742; +} + +#uploadStatusIcon p.text-warning { + color: #e59307; +} + +#uploadStatusIcon p.text-success { + color: #429742; +} + +#wrapper{ + opacity:1; + -webkit-transition-timing-function: cubic-bezier(0.68, -0.25, 0.265, .85); + transition-property:transform,opacity,box-shadow,top,left; + transition-duration:.5s; + transform-origin:161px 100%; + background: #35394a; /* Old browsers */ + background: -moz-linear-gradient(45deg, #35394a 0%, #1f222e 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left bottom, right top, color-stop(0%,#35394a), color-stop(100%,#1f222e)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* IE10+ */ + background: linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#35394a', endColorstr='#1f222e',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ +} + +#wrapper nav.navbar{ + border-width: 0px; +} + +.panel-primary { + border-color: #2c95dd; +} + +.panel-primary .panel-heading { + border-color: #2c95dd; + background-color: #2c95dd; +} + +.panel-success { + border-color: #5CB85C; +} + +.panel-success .panel-heading { + border-color: #5CB85C; + background-color: #5CB85C; + color: #ffffff; +} + +.panel-warning .panel-heading { + border-color: #FECC66; + background-color: #FECC66; + color: #ffffff; +} + +.panel-error { + border-color: #CC6666; +} + +.panel-error .panel-heading { + background-color: #CC6666; + color: #ffffff; +} + +.gray-bg { + background-color: #f8f8f8; +} + +.white-bg { + background-color: #ffffff; +} + +.panel-footer i { + color: #FECC66; + font-size: 2em; +} + +.templates i { + /* color: #ffffff; */ + font-size: inherit; +} + +table { + table-layout: fixed; +} + +table td{ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.tableCheckBoxCol { + width: 30px; +} + +#templatesToBeRemoved { + max-height: 250px; + overflow-y: auto; +} + +.recursiveCheckbox { + margin-left: 5px; +} + +.tooltip +{ + opacity: 1; +} + +/**************************************** Tooltips ***********************************************/ +.tooltip.in { + opacity: 1; + filter: alpha(opacity=100); +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-top-color: #238cd5; + border-width: 5px 5px 0; +} + +.tooltip-inner { + color: #238cd5; + background: #fff; + border: solid 1px #238cd5; + min-width: auto; + opacity: 1; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-left-color: #238cd5; + border-width: 5px 0 5px 5px; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-right-color: #238cd5; + border-width: 5px 5px 5px 0; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-bottom-color: #238cd5; + border-width: 0 5px 5px; +} + +/**************************************** Metadata ***********************************************/ +.totalMatchesTag { + margin: 0; + padding: 0 2px 0 0; +} + + +/**************************************** Dashboard ***********************************************/ +.progress-bar-success { + background-color: #2c95dd; + border: 1px solid #eeeeee; + border-radius: 4px; +} + +.progress { + background-color: #f9f9f9; + border: 1px solid #eeeeee; +} + +.serverIcon { + float: left; + padding: 0; + padding-left: 1%; + padding-right: 2%; +} + +#storageChart .arc{ + fill: #3580BD; +} + +#numberOfFilesDiv { + text-align: right; +} + +#uploadStatusIcon .progress{ + height: 24px; + margin-bottom:10px; +} + +/************************************* Collection Management **************************************/ +#inputFiles { + display: none; +} + +.pagination { + margin: 0; +} + +#goToPageInput { + padding: 5px; + width: 50px; +} + +#paginationCsvControl { + margin-bottom: 8px; +} + +h3.collection-details +{ + margin-top:0; + padding-bottom: 10px; +} +/**************************************** Switch Button *******************************************/ +.switch { + display: inline-block; + position: relative; + border-radius: 3px; + height: 26px; + width: 120px; + background: rgba(0, 0, 0, 0.25); + bottom: 0; + margin-top: 12px; +} + +.switch-label { + position: relative; + z-index: 2; + float: left; + width: 58px; + line-height: 26px; + font-size: 11px; + color: #ffffff; + text-align: center; + cursor: pointer; +} + +.switch label { + font-weight: normal; +} + +.switch-label-off { + padding-left: 2px; +} + +.switch-input { + display: none; +} +.switch-input:checked + .switch-label { + font-weight: bold; + color: rgba(0, 0, 0, 0.65); + -webkit-transition: 0.15s ease-out; + -moz-transition: 0.15s ease-out; + -o-transition: 0.15s ease-out; + transition: 0.15s ease-out; +} +.switch-input:checked + .switch-label-on ~ .switch-selection { + left: 60px; +} + +.switch-selection { + display: block; + position: absolute; + z-index: 1; + top: 2px; + left: 2px; + width: 58px; + height: 22px; + background: #ffffff; + border-radius: 3px; + -webkit-transition: left 0.15s ease-out; + -moz-transition: left 0.15s ease-out; + -o-transition: left 0.15s ease-out; + transition: left 0.15s ease-out; +} + + +/************************************* Server Details *********************************************/ +.list #resourcesByServerList ul a { + text-decoration: none; + color: inherit; +} + +.list ul li:hover, +.list ul li:active { + background-color: #f8f8f8; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + cursor: pointer; +} + +.list ul li { + list-style-type: none; +} + +.list ul li i { + float: right; +} + +.list div { + background-color: #ffffff; + margin: 0; + /* padding: 3%; + width: 100%; */ +} + +#partitionsList svg { + margin: 0; + padding: 0; + padding-top: 5%; + height: 180px; +} + +.list ul { + margin: 0; + padding: 0; +} + +.list ul li { + border-bottom: 1px solid #e7e7e7; + padding: 2% 2% 6% 2%; + margin: 0; + list-style: none; +} + +.list ul li img{ + float: left; + padding: 0; + padding-right: 1%; +} + +.list ul li p{ + padding-top: 5px; +} + +.bookmarksList i { + color: #428bca; +} +/************************************* Resources By Server ****************************************/ + +#resourcesByServerList img:first-child { + padding-bottom: 0%; + width: 44px; +} + +#resourcesByServerList { + /* border: 1px solid #e7e7e7; + border-radius: 4px; */ + margin-bottom: 10%; + background-color: #fff; +} +#resourcesByServerList .resourcesTableWrapper{ + border: 1px solid #eee; + border-radius: 5px; + padding: 5px; + margin: 20px 0; + overflow: auto; + height: 160px; +} +#resourcesByServerList .resourcesTableWrapper table{ + table-layout: auto; +} + +#resourcesByServerList .resourcesTableWrapper table tbody tr td{ + border-top: 1px dotted #ccc; + line-height: 2.5em; +} +#resourcesByServerList .resourcesTableWrapper table thead tr th{ + line-height: 2.5em; +} +#resourcesByServerList .resourcesTableWrapper table > tbody > tr > td:nth-child(3){ + width: 1%; + padding-right: 20px; +} +#resourcesByServerList .resourcesTableWrapper table > tbody > tr span, +#resourcesByServerList .resourcesTableWrapper table > thead > tr > th span{ + padding: 5px 5px 5px 20px; +} + +/********************************** User form - Permissions ***************************************/ + +.inheritance-checked { + color: #2c95dd; +} + + +/********************************** RESOURCE MAP CSS *******************************************/ + +.node circle { + fill: #fff; + stroke: steelblue; + stroke-width: 3px; +} + +.node, .link { + cursor: pointer; +} + +.link { + fill: none; + stroke: #ccc; + stroke-width: 2px; +} + +#resourceMapPanel { + max-width: 100%; + max-height: 50em; + overflow: auto; + padding: 0; + margin-top: 1%; + border: 1px solid #CBDFF7; + border-radius: 4px; + background: #fff; +} + +#resourceMapDashboard #resourceMapPanel{ + max-height: 530px; + background-color: #f2f6f9; + border: none; +} + +#contextMenu { + border: 1px solid #ccc; + border-radius: 4px; + background: #f8f8f8; + padding: 0; +} + +#contextMenu li{ + border-bottom: 1px solid #ccc; + padding: 0; + margin: 0; +} + +#contextMenu li a{ + color: #666; +} + +#contextMenu li a:hover{ + color: #fff; +} + +#contextMenu li:last-child { + border: 0; +} + +#freeSpaceBar { + margin-bottom: 1%; +} + +/********************************** CUSTOM DATATABLE CSS *******************************************/ + +#groupPermissionsTable_wrapper, #userPermissionsTable_wrapper{ + margin-top:0px; +} +#metadataSearchTableResults div.dataTables_wrapper div.dataTables_info { + text-align: right; +} +div.dataTables_wrapper.dt-bootstrap div.dataTables_paginate{ + text-align: center; + background-color: #5faee5; + height: 50px; +} +div.dataTables_wrapper.dt-bootstrap .row:last-of-type div.dataTables_paginate{ + padding-top: 10px; + border-bottom-left-radius:3px; + border-bottom-right-radius:3px; +} +div.dataTables_wrapper.dt-bootstrap div.dataTables_paginate ul.pagination{ + margin:0; +} +.table.table-bordered > thead > tr > th, .table-bordered > thead > tr > td { + border-bottom: 0 none; +} +div.dataTables_wrapper .row .col-md-12:first-child > div.dataTables_processing{ + position:absolute; + top:50%; + left:50%; + width:100%; + margin-left:-49%; + margin-top:-25px; + text-align:center; + font-size:1.2em; + color: dimgrey; + height: 60px; + background-color:white; + background:-webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255,255,255,0)), color-stop(25%, rgba(255,255,255,0.9)), color-stop(75%, rgba(255,255,255,0.9)), color-stop(100%, rgba(255,255,255,0))); + background:-webkit-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%); + background:-moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%); + background:-ms-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%); + background:-o-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%); + background:linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,0.9) 25%, rgba(255,255,255,0.9) 75%, rgba(255,255,255,0) 100%) +} + +div.dataTables_wrapper .row .col-md-12:first-child > div.dataTables_processing.loading-xs{ + top: 85px; + margin-top:-33px; + height: 30px; +} +div.dataTables_wrapper .row .col-md-12:first-child > div.dataTables_processing.loading-sm{ + margin-top:-10px; + height: 70px; +} +div.dataTables_wrapper .row .col-md-12:first-child > div.dataTables_processing.loading-md{ + height: 80px; +} + +div.dataTables_wrapper .row .col-md-12:first-child > div.dataTables_processing > img{ + width: 35px; + margin-top: 5px; +} +div.dataTables_wrapper .row .col-md-12:first-child > div.dataTables_processing.loading-xs > img{ + width: 30px; + margin-top: -6px; +} +div.dataTables_wrapper .row .col-md-12:first-child > div.dataTables_processing.loading-sm > img{ + width: 35px; +} +div.dataTables_wrapper .row .col-md-12:first-child > div.dataTables_processing.loading-md > img{ + width: 55px; + margin-top: 10px; +} + +div.dataTables_wrapper .row .col-md-12:first-child > div{ + background-color: #5faee5; + height: 50px; + padding-top: 10px; + color:white; +} + +div.dataTables_wrapper .row:first-child .col-md-12:first-child > div:first-child { + border-top-left-radius:3px; +} + +div.dataTables_wrapper .row:first-child .col-md-12:first-child > div:last-child { + border-top-right-radius:3px; +} + +div.dataTables_wrapper div.col-md-12 > .dataTables_info { + text-align: right; + font-size: 12px; + background-color: #5faee5; + color: white; + padding-right: 15px; + padding-top: 0; + padding-bottom: 5px; +} +.row table.dataTable{ + margin-top: 0px !important; + margin-bottom: 0px !important; +} +table.dataTable { + border: 1px solid #c8dff7; +} +div.dataTables_wrapper .download_csv{ + margin-bottom: 10px; +} +div.dataTables_wrapper .toolbar .input-group{ + display: inline-flex; +} +.dataTable th, .dataTable td { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +#groupsListTable tbody, #usersListTable tbody{ + background-color: white; +} +.table.dataTable thead tr { + background-color: #f1f1f1; + color: dimgrey; + border: none; + font-weight: bold; +} +.table.dataTable thead tr > th { + padding: 13px; + font-size: 15px; +} + +.table.dataTable thead tr > th.tableCheckBoxCol { + padding-left: 10px; +} + +.table.dataTable tr>td { + border-top: 1px dotted #c8dff7; + border-bottom: 1px dotted #c8dff7; + background-color: #fff; + padding: 10px; +} +.table.dataTable tr>td input[type=text]{ + width:100%; +} +#permissionTable { + width: auto; +} +#groupCollectionsList ul li, #groupCollectionsList.list div.bookmarksList { + padding: 0; + background: #f9fcfe; + border: 1px solid #bce8f1; +} +#groupCollectionsList ul li p { + padding: 15px 0 0 10px; +} +#groupCollectionsList.list div div { + padding: 10px 35px; + border-top: 1px dotted #ccc; +} +#groupCollectionsList.list div .table { + padding: 10px 30px; + border-top: 1px dotted #ccc; + margin: 0; + background-color: #fff; +} + +.confirm-msg-table{ + padding: 3px; + background: #f2f6f9; + border-radius:3px; +} + +.confirm-msg-table .buttons{ + display: flex; + justify-content: flex-end; +} + +.confirm-msg-table .btn{ + margin-right: 5px; + float: right; + min-width: 70px; +} + +#templateFieldsListTable_info { + text-align: left; + font-size: 14px; + padding-top: 8px; +} + +#templateFieldsList .requiredHeaderSpan{ + font-size:10px; + font-weight: normal; + margin-left: 5px; +} + +/**************************************************************************************************/ +.required:BEFORE { + content: "*"; +} + +/* Making sure the tooptip is not hidden by other layers */ +.tooltip { + position: fixed; +} + +.legend img { + float: left; + margin-right: 5%; +} + +#actions .btn-group, #addPermissionButtonDiv{ + margin: 0 15px 0 0; + padding: 0; +} +#actions .dropdown-menu .divider{ + margin: 5px 0; +} + +#addPermissionButtonDiv .dropdown-menu{ + top:30px; +} + +#actions .btn-group a:FIRST-CHILD{ + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +#actions .btn-group ul li.divider { + margin: 0; +} +#actions .btn-group ul li span, #addPermissionButtonDiv ul li span { + display: inline-block; + width: 20px; +} +#actions .btn-group ul li a, #addPermissionButtonDiv ul li a{ + color: #337ab7; +} + +#uploadBtn{ + margin-right: 2%; +} + +#actions i, #metadataActions i { + margin-right: 0.1%; +} + +#metadataActions{ + margin-right: 15px; +} + +#metadataActions > button{ + min-width: 80px; +} + +#metadataModal button { + min-width: 110px; +} + +#addMetadataBtn{ + margin-left: 10px; +} + +.saveEditAVUActionColumn, +.cancelEditAVUActionColumn{ + min-width: 40px; + margin-right: 5px; +} + +.saveEditAVUActionColumn > i, +.cancelEditAVUActionColumn > i{ + margin-right: 5px; +} + +.shortcuts-section * { + margin-bottom: 2%; +} + +th{ + cursor: pointer; +} + +.hideElement { + display: none; +} + +.errorMsg { + color: #a94442; + font-size: 11px; +} + +.permission-enabled, +.permission-icon:hover { + color: #2c95dd; +} + +.permission-disabled { + color: #c6c6c6; +} + +.permission-icon { + padding-right: 4%; + cursor: pointer; + font-size: 12pt; +} + +#collectionListAsync a{ + margin-right: 2%; +} + +#collectionListAsync .permission-icon{ + padding: 0; + margin-right: 1%; + cursor: default; + font-size: 8pt; +} + +#collectionListAsync .permission-icon:hover { + color: #c6c6c6; + cursor: default; +} + +#collectionListAsync .permission-enabled:hover { + color: #2c95dd; +} + +h1.page-header { + color: #428bca; + border-bottom:none; + padding-bottom: 0; + margin:30px 0 30px 0; +} + +h1 + span.subtitle +{ + margin-top: 45px; + margin-left:10px; + float: left; +} + +#info-collection label:AFTER { + content: ':'; +} +.collLesserInfo{ + clear:both; + color: #656363; +} +.collLesserInfo.divider{ + border-bottom: 1px dotted #CBDFF7; + margin:0 5px 15px 0; +} +.collLesserInfo > ul li{ + display: inline; + margin-left: 10px; + /* height: 10px; */ +} + +#replicaAndChecksumInfo .table thead tr{ + background-color: #59b0ee; + color: white; +} +#replicaAndChecksumInfo .table thead tr th{ + border: 1px solid transparent; + font-weight: bold; +} + +.tt-menu { + width: 200px; + margin-top: 12px; + padding: 8px 0; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); +} + +.tt-suggestion { + padding: 3px 20px; + line-height: 24px; +} + +.tt-suggestion { + padding: 3px 20px; + font-size: 12pt; + line-height: 24px; +} + +.tt-suggestion:hover { + cursor: pointer; + color: #fff; + background-color: #2c95dd; +} + +.tt-suggestion.tt-cursor { + color: #fff; + background-color: #2c95dd; +} + +.tt-suggestion p { + margin: 0; +} + +.empty-message { + padding: 5px 10px; + text-align: center; +} + +span .label { + padding: 0.4em .6em .3em; +} + +/********************************** BREADCRUMB *******************************************/ +.breadcrumb-wrapper { + background: #fff; + padding: 0; + border-radius: 4px; + border: solid 1px #f3f3f3; + border-color: #CBDFF7; + height: 40px; + margin: 5px 0 10px 0; + float: left; + width: 100%; +} + +.btn-group.history { + vertical-align: middle; + float: left; + display: inline-block; +} + +.btn-group.history>.btn.dropdown-toggle { + padding-right: 6px; + padding-left: 7px; + border-radius: 0; + position: relative; + height:38px; + border-left: none; + border-top: none; + border-bottom: none; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.btn-group.history>ul.dropdown-menu { + left: 0; + top: 38px; + width: auto; + min-width: 300px; +} + +.btn-group.history>ul.dropdown-menu>li.header{ + color:#808080; + font-style:italic; + padding-left:10px; +} + +.btn-group.history>.btn.dropdown-toggle { + border-right: 2px solid #f3f3f3; +} + +.btn-group.history>.btn.dropdown-toggle > .caret +{ + margin-bottom:5px; +} + +.dropdown-menu>li>a.home:before{ + font-family: FontAwesome; + content: "\f015"; + margin-right:5px; +} + +#editable-path { + display: inline-block; + min-height: 38px; + z-index: 4; + width:78%; +} + +#current-path { + display: inline-block; + width: 100%; + height: 38px; + padding: 0px; +} + +.breadcrumb{ + font-size: 17px; + padding: 7px 15px 0 7px;; + background-color: #fff; + border-radius: 0; + height:38px; +} + +.breadcrumb span{ + color: #333; +} + +.breadcrumb li { + overflow: hidden; + text-overflow: ellipsis; + max-width: 10ch; + white-space: nowrap; +} + +.breadcrumb li:last-child { + max-width: 25ch; +} + +#navigationBar{ + border: none; + margin-bottom: 10px; + padding: 3px 10px 0 10px + margin-left:0px; + background-color: #fff; + width:100%; + position:relative; +} +#navigationBarGo, +#navigationBarCancel{ + top:10px; + position:absolute; + +} + +#navigationBarGo{ + right:55px; +} + +#navigationBarCancel{ + right: 20px; +} + +#navigationInput{ + border: 3px solid #f7fafe; + background-color: #f7fafe; + border-radius: 0; + font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; + font-size: 15px; + width: 100%; + height:38px; + padding-left: 5px; +} + +.btn-favorite { + padding: 5px; + background-color: #fff; + border: none; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + display: inline; + float: right; +} + +#breadcrumbStar i, #breadcrumbHome i{ + font-size: 20px; + padding-right: 5px; +} + +#breadcrumbStar i +{ + padding-top:5px; +} + +#breadcrumbStar .bm-checked { + color: #f4d03f; +} +#breadcrumbStar .bm-unchecked { + color: #cbdaf0; +} + +#directoryPath #breadcrumbOptions .dropdown-menu{ + min-width: 130px; + right: -2px; +} +#directoryPath .dropdown-menu span{ + color: #337ab7; + display: inline-block; + width: 18px; +} + +#directoryPath a { + margin: 0; +} + +.folder-icon { + padding-right: 1%; +} + +ul{ + list-style-type: none; +} + +.btn-group.properties { + vertical-align: middle; + float: right; + display: inline-block; + height: 38px; +} + +.btn.btn-properties.btn-default.dropdown-toggle { + border-left: 2px solid #f3f3f3; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-bottom: none; + border-top: none; + border-right: none; + height: 38px; +} + +.btn.btn-properties>i{ + margin-top:5px; + font-size:15px; +} + +.dropdown-menu>li>a { + padding: 3px 10px; + font-size: 13px; + color: #337ab7; + vertical-align: middle; +} + +/* breadcrumb - search results */ +.breadcrumb.mlx{ + font-size:13px; + background-color:#f2f6f9; + float:right; + margin-bottom:0; + margin-top:30px; + margin-right:10px; + padding:0; + +} +.breadcrumb.mlx > li + li:before { + content: ">"; +} + + +a.page-hint { + font-size:1.3em; + margin-top:30px; +} + +.property{ + background: #fff; + padding: 15px 15px 25px 15px; + border-radius: 4px; + border: solid 1px #CBDFF7; +} + +.panel.loading{ + background: #fff; + padding: 15px 25px 15px 25px; + border-radius: 4px; + border: solid 1px #CBDFF7; +} + +.property-title { + color: #428bca; + float:left; + margin-left: 20px; + margin-right: 5px; +} + +.property-title i{ + margin-right: 5px; +} + +.btn-property, +.btn-page-action{ + margin-bottom: 10px; +} + +#tree-view-panel-body a, +#tree-view-panel-body a:hover, +#tree-view-panel-body a:ACTIVE { + text-decoration: none; +} + +#tree-view-panel span { + padding-top: 1%; +} + +#tree-view-panel a { + margin: 0; + padding: 0; +} + +#emptyTrashBtn{ + height:40px; + margin-top:5px; + padding-top: 10px; +} +#actionUpload > a{ + width:50px; + height:40px; + margin-top:5px; + display: flex; +} + +#showCollectionFormBtn{ + margin-left: 5px; + display: flex; + justify-content:center; +} + +#showCollectionFormBtn > span{ + margin-right: 1px; + float:left; +} +#showCollectionFormBtn > span i.icon-inside { + color: #428bca; + margin-top: 2px; +} +#showCollectionFormBtn span.fa-stack { + font-size: 11px; + margin-top:3px; +} +#uploadIcon div.btnLabel, +#showCollectionFormBtn div.btnLabel { + padding-top: 4px; + visibility:hidden; + display:none; +} + +#uploadIcon div.btnLabel{ + padding-left: 3px; +} + +#showCollectionFormBtn div.btnLabel { + padding-left: 5px; +} + +#uploadIcon{ + float:right; + display: flex; + justify-content:center; +} + +#uploadIcon > i{ + font-size: 21px; + margin-right:3px; + margin-top:3px; +} + +.panel-info{ + padding: 15px; + background-color: #f2f6f9; + border-radius: 5px; +} + +.panel-info span{ + word-break: break-all; +} + +.panel-info > .name { + font-size: 15px; + margin-bottom: 10px; +} + +.btn-default.disabled, +.btn-default[disabled]{ + background-color: #fff; + color: #cbdff7; + border: solid 1px #cbdff7; +} +.btn-default.disabled > i{ + opacity: 1 !important; +} + +.btn-users, +.btn-groups, +.btn-templates, +.btn-resources { + float: right; + margin-bottom: 10px; + min-width: 120px; +} +a.btn-add-user{ + margin-right: 15px; + float: right; + min-width: 120px; +} +.btn-shared, .btn-resources-view, .btn-template-action{ + float: left; + margin-right: 5px; + margin-bottom: 10px; + min-width: 120px; +} + +.clear { + clear: both; +} + +#actionsWait{ + margin-top:7px; + width: 300px; +} + +div.dataTables_wrapper .dataTables_length{ + float:left !important; +} +div.dataTables_wrapper .dataTables_length select{ + width:65px !important; + font-size:12px; + margin-right:10px; + height:35px; +} + +div.dataTables_wrapper div.dataTables_filter{ + text-align: none; + float:right; +} + +alert-dialog { + display: none; + visibility:hidden; +} + +.alert{ + padding:5px; + margin-bottom: 10px; +} + +.alert-dismissable .close, +.alert-dismissible .close +{ + position: relative; + top: -2px; + right: 0px; + color: inherit; +} + +.table-loader { + padding: 0 15px; +} + +.table-loader img { + width: 40px; +} + +.table-loader > p { + padding: 1%; +} + +.scrollable-table { + max-height: 600px; + overflow-x: auto; +} + +.scrollable-div { + height: 38em; + overflow: hidden; + overflow-y: auto; +} + +.scrollable-modal-body { + max-height: 300px; + overflow: auto; +} +.modal .modal-header{ + background-color: #428bca; + color: #fff; + border-radius: 5px 5px 0 0; +} +#copyModal{ + overflow:hidden; +} +#copyModal .modal-body, #moveModal .modal-body{ + max-height:70vh; + overflow:auto; +} + +.table { + margin-top: 1%; +} + +.table-responsive { + border: 0; +} + +.navbar-brand { + padding: 10px 0 0 0; +} + +.nav .open>a, .nav .open>a:hover, .nav .open>a:focus, { + background-color: #2786c6; + border-color: #2786c6; +} + +.subtitle { + color: #777; +} + +.btn-primary, .panel-primary > .panel-heading { + background: #2c95dd; + border-color: #2c95dd; +} + +.btn-primary:hover { + background: #2786c6; +} + +.navbar{ + background: #2c95dd; +} + +.navbar-toggle{ + background: #2c95dd; + border: 0; +} + +.navbar-default .navbar-toggle .icon-bar { + background: #fff; +} + +.white-icon{ + color: #fff; +} + +#wrapper { + width: 1026px; + margin: 0 auto; +} + +#page-wrapper, #page-wrapper-tickets { + padding: 0 15px; + min-height: 568px; + background-color: #f2f6f9; +} + +#page-wrapper:last-child, #page-wrapper-tickets:last-child{ + margin-bottom: 10%; +} + +.navbar-top-links-text { + color: #fff; +} + +.navbar-top-links li { + display: inline-block; +} + +.navbar-top-links li:last-child { + margin-right: 15px; +} + +.navbar-top-links li a { + padding: 15px; + min-height: 50px; + color: #fff; +} + +#side-menu li.profile: first-child span{ + bottom:0; + margin-bottom: 5px; +} + +i.fa-square-o.menu-icon{ + font-size: 42px; +} +i.fa-user.menu-icon{ + font-size: 25px; +} + +#uploadStatusIcon{ + padding-right: 0; + position: absolute; + top: 0; + right: 100px; +} +#uploadStatusIcon li a{ + color: #656363; + padding-right: 0; + top:-1; +} + +ul.navbar-top-links > li.dropdown > .dropdown-toggle:hover, +ul.navbar-top-links > li.dropdown > .dropdown-toggle:focus, +ul.navbar-top-links > li.open > a.dropdown-toggle{ + background-color: #2c95dd; + box-shadow: inset 0 3px 10px rgba(0,0,0,.125); + border-top: none; + border-bottom: none; +} + +.navbar-top-links .dropdown-menu li { + display: block; +} + +.navbar-top-links .dropdown-menu li:last-child { + margin-right: 0; +} + +.navbar-top-links .dropdown-menu li a { + padding: 3px 20px; + min-height: 0; +} + +.navbar-top-links .dropdown-menu li a div { + white-space: normal; +} + +.navbar-top-links .dropdown-messages, +.navbar-top-links .dropdown-tasks, +.navbar-top-links .dropdown-alerts { + width: 310px; + min-width: 0; +} + +.navbar-top-links .dropdown-messages { + margin-left: 5px; +} + +.navbar-top-links .dropdown-tasks { + margin-left: -59px; +} + +.navbar-top-links .dropdown-alerts { + margin-left: -123px; +} + +.navbar-top-links .dropdown-user { + right: 0; + left: auto; + background-color: #2c95dd; + margin-top: 0; +} +.navbar-top-links .dropdown-user > li > a{ + color: white; +} +.navbar-top-links .dropdown-user > li > a:hover{ + background-color: #3d9de0; +} + +.sidebar .sidebar-nav.navbar-collapse { + padding-right: 0; + padding-left: 0; +} + +.sidebar .sidebar-search { + padding: 15px; +} + +.sidebar #side-menu{ + opacity:1; + -webkit-transition-timing-function: cubic-bezier(0.68, -0.25, 0.265, .85); + transition-property:transform,opacity,box-shadow,top,left; + transition-duration:.5s; + transform-origin:161px 100%; + transform:rotateX(0deg); + background: #35394a; /* Old browsers */ + background: -moz-linear-gradient(45deg, #35394a 0%, #1f222e 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left bottom, right top, color-stop(0%,#35394a), color-stop(100%,#1f222e)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* IE10+ */ + background: linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#35394a', endColorstr='#1f222e',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */ +} + +.sidebar ul li a{ + color: #b0bcc5; + border-right-width: 6px; + border-right-style: solid; + border-right-color: #35394a; /* Old browsers */ + border-right-color: -moz-linear-gradient(45deg, #35394a 0%, #1f222e 100%); /* FF3.6+ */ + border-right-color: -webkit-gradient(linear, left bottom, right top, color-stop(0%,#35394a), color-stop(100%,#1f222e)); /* Chrome,Safari4+ */ + border-right-color: -webkit-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* Chrome10+,Safari5.1+ */ + border-right-color: -o-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* Opera 11.10+ */ + border-right-color: -ms-linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* IE10+ */ + border-right-color: linear-gradient(45deg, #35394a 0%,#1f222e 100%); /* W3C */ + padding: 5px 0 5px 0; +} + +.sidebar ul li a:hover, .sidebar ul li a:focus, .sidebar ul li a.active{ + background: #35394a; + border-right: 6px solid #2c95dd; +} + +.sidebar .arrow { + float: right; +} + +.sidebar .fa.arrow:before { + content: "\f104"; +} + +.sidebar .active>a>.fa.arrow:before { + content: "\f107"; +} + +.sidebar .nav-second-level li, +.sidebar .nav-third-level li { + border-bottom: 0 !important; +} + +.sidebar .nav-second-level li a { + padding-left: 37px; +} + +.sidebar .nav-third-level li a { + padding-left: 52px; +} + +#side-menu li a span{ + text-align: right; + width:100%; + bottom: -6px; +} + +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #2786c6; +} + +.navbar-default .navbar-toggle { + border: 0; + border-color: none; +} + +.modal-footer button.btn{ + min-width: 100px; +} + +button.btn-default > i, +a.btn-default > i, +button.btn-primary > i, +a.btn-primary > i { + margin-right: 3px; +} + +.user-panel { + padding: 5%; + padding-left: 5%; + background: #fff; +} + +.user-panel img{ + float: none; + margin: 0 auto; + width: 75px; + height: 75px; +} + +.img-circle { + border-radius: 65%; +} + +.user-panel p { + padding: 3%; +} + +.user-panel p span { + margin-left: 3%; +} + +#uploadForm > .text-right{ + font-size:11px; + margin-bottom:1px; +} +#uploadForm > .well{ + max-height:333px; + overflow:auto; + min-height: 200px; + text-align: center; +} +#uploadForm > .well #filesList{ + text-align: left; +} +#uploadForm .col-lg-3{ + height:26px; +} +#tree-view-panel-body .tab-pane{ + /* margin-left: 10px; + margin-right: 5px; */ +} +#emptyMetadataTable{ + text-align:center; +} + +.metadata > .tree-view-panel > .tabpanel { + margin-left: 0; + padding-left: 0; + margin-bottom: 5px; + padding-bottom: 10px; +} +#properties-search { + padding-bottom: 10px; +} +#metadata-search > div, #properties-search > div{ + display:inline-block; + width:100%; +} +#metadataSearchActionButtons{ + margin-bottom: 10px; +} + +#propertiesSearchActionButtons button, +#metadataSearchActionButtons button{ + margin-left:10px; + min-width: 110px; +} + +.metadataSearchField{ + width: 23%; +} + +.propertiesSearchField { + width: 32%; +} + +.metadataSearchRow, .propertiesSearchRow{ + margin:5px 0; + justify-content: space-between; + display: flex; +} +.modal .tab-content{ + padding-top:10px; + padding-bottom:10px; + background-color: #f2f6f9;; +} + +.modal .nav-tabs > li.active > a, +.modal .nav-tabs > li.active > a:hover, +.modal .nav-tabs > li.active > a:focus, +.modal .nav > li > a:hover, +.modal .nav > li > a:focus{ + background-color: #f2f6f9; + border: 1px solid #f2f6f9; +} +.nav-tabs { + border-bottom: 1px hidden #DDD; +} +.modal-footer .targetPathInfoDiv{ + margin-bottom: 10px; +} +.modal-footer .targetPathInfoSpan{ + font-weight: bold; +} + +#templateListAsync #actions{ + width: 50%; +} + +#templateListAsync .scrollable-table{ + margin-top: 50px; +} + +#ui-datepicker-div{ + font-size:14px; +} + +footer { + text-align: center; + font-size:12px; + width: 100%; + color: #777; + margin-top: -20px; +} + +.dashboardWidget .panel{ + border-radius: 5px; + border-color: #CBDFF7; +} +#systemHealth .panel.panel-default{ + padding: 10px; +} +#systemHealth .panel .panel-body{ + padding: 0; +} +.rmd-status-msg{ + padding: 0 10px; + color: #f0ad4e; +} +#systemHealth .panel .panel-body h4{ + margin-top: 0; + margin-bottom: 0; +} +.grid-status-title.normal{ + color: #5CB85C; +} +.grid-status-title.error{ + color:#d9534f; +} +.grid-status-title.warning{ + color: #f0ad4e; +} +#totalStorageDiv #totalStorageSum table{ + width: 100%; +} +.panel.totalStorage{ + height: 310px; +} +#totalStorageSum table > tbody > tr > td:first-child { + text-align: right; + width: 50%; +} +#totalStorageSum table > tbody > tr > td:nth-child(2) > span { + margin-left: 3px; +} +.resourceMapDashboardWrapper{ + height: 630px; +} +.resourceMapDashboardWrapper .panel{ + border-radius: 5px; + margin-bottom: 10px; + border: 1px solid #CBDFF7; +} +.resourceMapDashboardWrapper .panel-body{ + height: 610px; +} + +.resourceMapDashboardWrapper #resourceInfo{ + z-index:1; +} + +.resourceMapDashboardWrapper a { + margin-top: 15px; +} + +#serverListPanel{ + height: 300px; +} + +#serverListPanel .panel { + height: 280px; +} + +#serverListPanel .panel .panel-body { + height: 230px; + overflow: auto; +} + +#msiAPIVersionPanel .popover{ + max-width: 550px; +} + +#mlxMsiPanel table td{ + overflow: auto; + text-overflow: initial; + white-space: normal; + border-top: 0px solid #DDD; + border-bottom: 1px solid #DDD; +} +#isilonServerListPanel, #isilonServerListPanel .panel, #nonResourceServerListPanel, #nonResourceServerListPanel .panel, #msiAPIVersionPanel .panel{ + height: 165px; +} +.dashboardCard { + color: #3580bd; + border-radius: 0px; +} +.dashboardCard .panel-body{ + text-align:center; + padding: 0px; +} +.dashboardCard .panel-body .cardIconWrapper{ + background-color: #3580bd; + position: absolute; + height: 65%; + top: 0px; + left: 16px; + width: 23%; + border-top-left-radius: 5px; + border-bottom-left-radius: 5px; +} +.dashboardCard .panel-body .cardIcon{ + font-size: 18pt; + padding-top: 9px; + color: #fff; +} +.dashboardCard .panel{ + border-color: #CBDFF7; + border-radius: 5px; +} +.dashboardCard .panel-body .fa-stack{ + width: 25%; + padding-top: 3px; + font-size: 13pt; +} +.dashboardCard .btn-link{ + margin-right: 14px; + margin-top: 1px; + font-size: 13pt; +} +#serverListPanel .dashboardServersList{ + width: 100%; + table-layout: auto; +} +#serverListPanel .dashboardServersList > tbody > tr > td, +#isilonServerListPanel .table tbody > tr > td, +#nonResourceServerListPanel .table tbody > tr > td{ + padding: 8px; + border-top: 1px dotted #C8DFF7; + white-space: normal; +} +#serverListPanel .dashboardServersList > tbody > tr > td:first-child{ + width: 1%; +} +#serverListPanel .dashboardServersList > tbody > tr > td > a{ + display: block; +} +#serverListPanel .dashboardServersList > tbody > tr > td > span.fa-stack > i.fa-circle.fa-stack-2x{ + color: #5CB85C; +} + +.networkWidgetPanel, .storageWidgetPanel{ + height: 220px; +} +.storageWidgetPanel table{ + margin: 50px 0; +} +#totalStorageSum.storageWidgetPanel table > tbody > tr > td:first-child{ + text-align: left; +} +#totalStorageSum.storageWidgetPanel table > tbody > tr > td:nth-child(2){ + text-align: right; +} +#totalStorageSum.storageWidgetPanel table > tbody > tr:nth-child(3){ + border-top: 1px solid lightgray; +} +#irodsServerLogs{ + background-color: #f2f6f9; +} +#irodsServerLogs p{ + word-break: break-all; +} + +#resourcesPanel .panel-body{ + height: 15em; + overflow: auto; +} +#resourceListOfAServer{ + background-color: #f2f6f9; + padding: 10px 0; +} +#resourceListOfAServer table tr{ + border-bottom: 1px dotted #C8DFF7; +} +#resourceListOfAServer table tr > td:nth-child(3){ + text-align: right; + padding-right: 10px; +} + +#treeViewTable tr td .recursiveQuestion, +#treeViewCollectionTable tr td .recursiveQuestion{ + position: absolute; + z-index: 99; + width: 100%; + background-color: #d9edf7; + left: 0; + margin-top: -9px; + height: 40px; + padding-top: 6px; +} +#treeViewTable tr td .recursiveQuestion > div, +#treeViewCollectionsTable tr td .recursiveQuestion > div{ + display: flex; + justify-content: center; +} +#treeViewTable tr td .recursiveQuestion .recursiveQuestionButtonsWrapper, +#treeViewCollectionsTable tr td .recursiveQuestion .recursiveQuestionButtonsWrapper{ + margin-left: 15px; +} +.form-group.optional{ + height: 70px; +} + +form.registerForm input.form-control::placeholder, +form.registerForm input.tt-input::placeholder { + color:#a3a3a3; +} + +form.registerForm input.tt-input{ + width: 100%; !important +} + +form.registerForm .panel-default > .panel-heading { + background-color: #fff; + border-top: 3px solid #5faee5; + height: 42px; +} +form.registerForm .panel-default > .panel-heading { + clear: both; +} +form.registerForm .panel-default > .panel-body table.dataTable{ + border: none; +} +form.registerForm .panel-default > .panel-body .table.dataTable thead tr{ + background-color: #fff +} +form.registerForm .panel-default > .panel-body div.dataTables_wrapper .row .col-md-12:first-child > div { + background-color: #f2f6f9; +} +form.registerForm .panel-default > .panel-body div.dataTables_wrapper .dataTables_info { + text-align: center; + color: #656363; +} +form.registerForm > .pull-right > .btn{ + min-width: 80px; + margin-left: 10px; + margin-bottom: 15px; +} +form.registerForm .bootstrap-tagsinput { + width: 100%; +} +.advanced-settings{ + clear: both; + padding: 15px; + border-radius: 5px; + background: #f8f8f8; + height: 840px; +} +#collectionsViewForUser .breadcrumb, #collectionsViewForGroup .breadcrumb{ + margin-left: 0px; +} + +.resulting-permission { + font-size: 12px; + color: gray; + padding-left: 5px; +} +#nonexistentUsernameIcon, #nonexistentGroupIcon { + left: 260px; +} + + +/* Medium devices and up */ +@media ( min-width : 1001px) { + #actionApplyTemplates, #actionDownload { + width: 350px; + } + + #directoryPath{ + padding-right:0; + } + + #actionRemoval{ + width: 270px; + } +} + +@media ( min-width : 769px) and (max-width: 1000px) { + + #treeViewTable { + float: left; + } + #actionUpload { + margin-bottom: 0px + } + #editable-path { + width:78%; + } + .breadcrumb:nth-last-child(2), + .breadcrumb:nth-last-child(3) { + visibility:hidden; + } +} + +@media ( max-width : 767px) { + .sidebar.nano > .nano-content { + position:relative; + } + .sidebar .fa-stack.fa-lg, .sidebar i.fa{ + display:none; + } + .sidebar ul li.menu-group-title{ + padding: 1px 0; + background: #3580bd; + font-size: .7em; + color: #f9f9f9; + width: 100%; + text-align: center; + border: 1px solid #3580bd; + font-family: Verdana, Geneva, sans-serif; + } + .scrollable-table { + max-height: 100%; + } + #treeViewTable{ + float: left; + } + #actionUpload { + margin-bottom: 0px + } + #treeViewTable{ + margin-top: 30px; + } + #editable-path { + width:65%; + } + #current-path{ + width:65%; + } + .breadcrumb li:last-child { + max-width: 80%; + } + #uploadIcon{ + float:left; + } + #actionUpload > a{ + width: 49%; + margin-top:5px; + margin-bottom:5px; + padding-top: 2px; + padding-bottom: 2px; + height: 35px; + } + #uploadIcon div.btnLabel, + #showCollectionFormBtn div.btnLabel { + visibility:visible; + display:block; + } +} + + +/* Medium devices and up */ +@media(min-width: 768px){ + .sidebar { + z-index: 1; + position: absolute; + width: 100px; + } + .sidebar.nano{ + position: absolute; + width: 100px; + height:100%; + } + .sidebar.nano > .nano-pane > .nano-slider{ + background: rgba(255, 255, 255, 0.5) none repeat scroll 0 0; + } + #page-wrapper, #page-wrapper-tickets { + position: inherit; + padding: 0 30px; + } + + #page-wrapper { + margin: 0 0 0px 100px; + border-left: 1px solid #e7e7e7; + } + #side-menu li a span.fa-stack.fa-lg + { + font-size:20px; + } + + #side-menu li a span.fa-stack.fa-lg > .fa-folder.fa-stack-2x + { + font-size:40px; + } + + .sidebar ul li a { + border-top-style: none; + text-align: center; + } + .sidebar ul li a i{ + display: block; + font-size: 30px; + } + .sidebar ul li:last-child a { + border-bottom-style: none; + } + .sidebar ul li.menu-group-title{ + padding: 1px 0; + background: #3580bd; + font-size: .7em; + color: #f9f9f9; + width: 100%; + text-align: center; + font-family: Verdana, Geneva, sans-serif; + } + + .navbar-top-links .dropdown-messages, .navbar-top-links .dropdown-tasks, + .navbar-top-links .dropdown-alerts { + margin-left: auto; + } + .user-panel { + padding: 10%; + padding-left: 5%; + background: #fff; + } + .user-panel img { + float: none; + margin: 0 auto; + width: 75px; + height: 75px; + } + .user-panel p { + padding: 8%; + } + .user-panel p span { + margin-left: 5%; + } + .list ul li { + padding: 1%; + } + .searchBox { + width: 340px; + position: absolute; + right: 14px; + top: 45px; + } + +} + +/* Large Devices */ +@media (min-width: 1200px) { + .breadcrumb li:nth-last-child(2), + .breadcrumb li:nth-last-child(3) { + max-width:15ch; + } + .breadcrumb li:last-child { + max-width: 25ch; + } + #editable-path { + width:85%; + } +} + +/* Button btn-default*/ + +.btn-default { + color: #2c95dd; + background-color: #fff; + border-color: #CBDFF7; +} + +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open>.dropdown-toggle.btn-default +{ + color: #2c95dd; + background-color: #ffffff; + border-color: #CBDFF7; +} + +.btn-group.open .dropdown-toggle { + box-shadow: inset 0 1px 1px rgba(0,0,0,.125); +} + +/* Tickets */ +#newTicketInfoCopy { + opacity:0; + width:1px; + height:1px; + float: left; +} + +#copyTicketStringBtn { + margin-left: 20px; +} + +#copyTicketStringBtn > i, +#sendTicketStringBtn > i { + margin-right:2px; +} + +#hostnamesLabel{ + width: 100%; +} + +#inputNewHostname{ + width:78%; + margin-bottom:10px; +} + +#panelHostnames { + clear:both; + height: 125px; + overflow: scroll; + background: #fff; + border: 1px solid #ccc; + margin-top: 8px; + border-radius: 5px; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} + +#listHostnames{ + padding: 2px; +} + +#listHostnames li{ + border-bottom: 1px dotted lightgray; + padding: 5px; +} + +#listHostnames li a.btn { + margin-top:-5px; +} + +#emailBtn, #ticketDownloadBtn { + margin-right: 5px; +} + +#ticketAccessForm button{ + min-width:120px; +} + +#existingIPMsg{ + clear: both; + float: left; + margin-top: -5px; +} +#existingIpIcon{ + left: 360px; +} diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/FontAwesome.otf b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/FontAwesome.otf new file mode 100644 index 000000000..3ed7f8b48 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/FontAwesome.otf differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/Lato-Regular.ttf b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/Lato-Regular.ttf new file mode 100644 index 000000000..04ea8efb1 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/Lato-Regular.ttf differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.eot b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..9b6afaedc Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.eot differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.svg b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..03ff8cc39 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.svg @@ -0,0 +1,671 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.ttf b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..26dea7951 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.ttf differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.woff b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..dc35ce3c2 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.woff differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.woff2 b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..500e51725 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/fontawesome-webfont.woff2 differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.eot b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000..4a4ca865d Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.eot differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.svg b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..d9fbc0788 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000..67fa00bf8 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.woff b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 000000000..8c54182aa Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/fonts/glyphicons-halflings-regular.woff differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Data-Network-48.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Data-Network-48.png new file mode 100644 index 000000000..11ab7f792 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Data-Network-48.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Server-error.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Server-error.png new file mode 100644 index 000000000..0afa91a8a Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Server-error.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Server-normal.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Server-normal.png new file mode 100644 index 000000000..f310456bf Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Server-normal.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Server-warning.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Server-warning.png new file mode 100644 index 000000000..918d3178a Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/Server-warning.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/ajax_loader.gif b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/ajax_loader.gif new file mode 100644 index 000000000..8f8ff0868 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/ajax_loader.gif differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/ajax_loader_button.gif b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/ajax_loader_button.gif new file mode 100644 index 000000000..53ab17741 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/ajax_loader_button.gif differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/avatar-blank.jpg b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/avatar-blank.jpg new file mode 100644 index 000000000..6dc2865bd Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/avatar-blank.jpg differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/chart_ex1.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/chart_ex1.png new file mode 100644 index 000000000..66afa149f Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/chart_ex1.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/chart_ex2.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/chart_ex2.png new file mode 100644 index 000000000..4e17fbba8 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/chart_ex2.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/code-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/code-16.png new file mode 100644 index 000000000..68f430e99 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/code-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/excel-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/excel-16.png new file mode 100644 index 000000000..90055f66c Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/excel-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/favicon-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/favicon-16.png new file mode 100644 index 000000000..daf32b8bf Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/favicon-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/file-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/file-16.png new file mode 100644 index 000000000..f38b8a18e Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/file-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/folder-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/folder-16.png new file mode 100644 index 000000000..fd53720e5 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/folder-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/folder-open-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/folder-open-16.png new file mode 100644 index 000000000..c7ced2198 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/folder-open-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/free-space-bar.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/free-space-bar.png new file mode 100644 index 000000000..7c917cbe8 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/free-space-bar.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/logo-blue.svg b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/logo-blue.svg new file mode 100644 index 000000000..6a1d9a341 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/logo-blue.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/logo.svg b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/logo.svg new file mode 100644 index 000000000..9d2ea8bd4 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/logo.svg @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/markup-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/markup-16.png new file mode 100644 index 000000000..ba08cdfee Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/markup-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/pdf-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/pdf-16.png new file mode 100644 index 000000000..c514c4dc5 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/pdf-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/photo-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/photo-16.png new file mode 100644 index 000000000..c65eba857 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/photo-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/ppt-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/ppt-16.png new file mode 100644 index 000000000..54d527e23 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/ppt-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/profile-image-display.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/profile-image-display.png new file mode 100644 index 000000000..79c39de2c Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/profile-image-display.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/rar-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/rar-16.png new file mode 100644 index 000000000..0c7d2c97a Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/rar-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/spinner-16.gif b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/spinner-16.gif new file mode 100644 index 000000000..1a47b9489 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/spinner-16.gif differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/square-green-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/square-green-16.png new file mode 100644 index 000000000..c2b8b8f4f Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/square-green-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/square-red-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/square-red-16.png new file mode 100644 index 000000000..891324b3d Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/square-red-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/table_loading.svg b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/table_loading.svg new file mode 100644 index 000000000..ad50e9290 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/table_loading.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/word-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/word-16.png new file mode 100644 index 000000000..a4786d2c9 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/word-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/zip-16.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/zip-16.png new file mode 100644 index 000000000..055b8fad6 Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/zip-16.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/images/zone-48.png b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/zone-48.png new file mode 100644 index 000000000..75c2c816f Binary files /dev/null and b/packaging/src/emc-metalnx-shared/src/main/resources/static/images/zone-48.png differ diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/js/ajax.js b/packaging/src/emc-metalnx-shared/src/main/resources/static/js/ajax.js new file mode 100644 index 000000000..491a03e29 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/js/ajax.js @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015-2017 Dell EMC + * + * Licensed 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. + * + */ + +/** + * JS file that has an Ajax method that encapsulates all Ajax calls. In this encapsulated call, + * we handle success, errors and session expired exceptions. + */ +function ajaxEncapsulation(url, method, params, successFunction, errorFunction, dataType, contentType, callbacks){ + if (contentType == null || typeof contentType === 'undefined') { + contentType = "application/x-www-form-urlencoded; charset=UTF-8"; + } + + $.ajax({ + url: url, + type: method, + contentType: contentType, + dataType: dataType, + data: params, + async: true, + cache: false, + success: successFunction, + error: errorFunction, + complete: function() { + $(".alert-danger").delay(12000).fadeOut('slow'); + $(".alert-warning").delay(12000).fadeOut('slow'); + $(".alert-success").delay(12000).fadeOut('slow'); + $("table .label-success").delay(4000).fadeOut('slow'); + $("table .label-warning").delay(4000).fadeOut('slow'); + $("table .label-danger").delay(4000).fadeOut('slow'); + }, + statusCode: { + 500: function(response){ + window.location= "/emc-metalnx-web/httpError/500/"; + }, + 408: function(response){ + window.location= "/emc-metalnx-web/login/"; + }, + 403: function(response){ + window.location= "/emc-metalnx-web/login/"; + }, + 503: function(response){ + window.location= "/emc-metalnx-web/httpError/serverNotResponding/"; + } + } + }).done(callbacks); +} + +function pageNotFound(response){ + alert(response); +} \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/js/bootstrap-tagsinput.min.js b/packaging/src/emc-metalnx-shared/src/main/resources/static/js/bootstrap-tagsinput.min.js new file mode 100644 index 000000000..16be0abb9 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/js/bootstrap-tagsinput.min.js @@ -0,0 +1,7 @@ +/* + * bootstrap-tagsinput v0.4.2 by Tim Schlechter + * + */ + +!function(a){"use strict";function b(b,c){this.itemsArray=[],this.$element=a(b),this.$element.hide(),this.isSelect="SELECT"===b.tagName,this.multiple=this.isSelect&&b.hasAttribute("multiple"),this.objectItems=c&&c.itemValue,this.placeholderText=b.hasAttribute("placeholder")?this.$element.attr("placeholder"):"",this.inputSize=Math.max(1,this.placeholderText.length),this.$container=a('
'),this.$input=a('').appendTo(this.$container),this.$element.after(this.$container);var d=(this.inputSize<3?3:this.inputSize)+"em";this.$input.get(0).style.cssText="width: "+d+" !important;",this.build(c)}function c(a,b){if("function"!=typeof a[b]){var c=a[b];a[b]=function(a){return a[c]}}}function d(a,b){if("function"!=typeof a[b]){var c=a[b];a[b]=function(){return c}}}function e(a){return a?i.text(a).html():""}function f(a){var b=0;if(document.selection){a.focus();var c=document.selection.createRange();c.moveStart("character",-a.value.length),b=c.text.length}else(a.selectionStart||"0"==a.selectionStart)&&(b=a.selectionStart);return b}function g(b,c){var d=!1;return a.each(c,function(a,c){if("number"==typeof c&&b.which===c)return d=!0,!1;if(b.which===c.which){var e=!c.hasOwnProperty("altKey")||b.altKey===c.altKey,f=!c.hasOwnProperty("shiftKey")||b.shiftKey===c.shiftKey,g=!c.hasOwnProperty("ctrlKey")||b.ctrlKey===c.ctrlKey;if(e&&f&&g)return d=!0,!1}}),d}var h={tagClass:function(){return"label label-info"},itemValue:function(a){return a?a.toString():a},itemText:function(a){return this.itemValue(a)},freeInput:!0,addOnBlur:!0,maxTags:void 0,maxChars:void 0,confirmKeys:[13,44],onTagExists:function(a,b){b.hide().fadeIn()},trimValue:!1,allowDuplicates:!1};b.prototype={constructor:b,add:function(b,c){var d=this;if(!(d.options.maxTags&&d.itemsArray.length>=d.options.maxTags||b!==!1&&!b)){if("string"==typeof b&&d.options.trimValue&&(b=a.trim(b)),"object"==typeof b&&!d.objectItems)throw"Can't add objects when itemValue option is not set";if(!b.toString().match(/^\s*$/)){if(d.isSelect&&!d.multiple&&d.itemsArray.length>0&&d.remove(d.itemsArray[0]),"string"==typeof b&&"INPUT"===this.$element[0].tagName){var f=b.split(",");if(f.length>1){for(var g=0;gd.options.maxInputLength)){var l=a.Event("beforeItemAdd",{item:b,cancel:!1});if(d.$element.trigger(l),!l.cancel){d.itemsArray.push(b);var m=a(''+e(i)+'');if(m.data("item",b),d.findInputWrapper().before(m),m.after(" "),d.isSelect&&!a('option[value="'+encodeURIComponent(h)+'"]',d.$element)[0]){var n=a("");n.data("item",b),n.attr("value",h),d.$element.append(n)}c||d.pushVal(),(d.options.maxTags===d.itemsArray.length||d.items().toString().length===d.options.maxInputLength)&&d.$container.addClass("bootstrap-tagsinput-max"),d.$element.trigger(a.Event("itemAdded",{item:b}))}}}else if(d.options.onTagExists){var o=a(".tag",d.$container).filter(function(){return a(this).data("item")===k});d.options.onTagExists(b,o)}}}},remove:function(b,c){var d=this;if(d.objectItems&&(b="object"==typeof b?a.grep(d.itemsArray,function(a){return d.options.itemValue(a)==d.options.itemValue(b)}):a.grep(d.itemsArray,function(a){return d.options.itemValue(a)==b}),b=b[b.length-1]),b){var e=a.Event("beforeItemRemove",{item:b,cancel:!1});if(d.$element.trigger(e),e.cancel)return;a(".tag",d.$container).filter(function(){return a(this).data("item")===b}).remove(),a("option",d.$element).filter(function(){return a(this).data("item")===b}).remove(),-1!==a.inArray(b,d.itemsArray)&&d.itemsArray.splice(a.inArray(b,d.itemsArray),1)}c||d.pushVal(),d.options.maxTags>d.itemsArray.length&&d.$container.removeClass("bootstrap-tagsinput-max"),d.$element.trigger(a.Event("itemRemoved",{item:b}))},removeAll:function(){var b=this;for(a(".tag",b.$container).remove(),a("option",b.$element).remove();b.itemsArray.length>0;)b.itemsArray.pop();b.pushVal()},refresh:function(){var b=this;a(".tag",b.$container).each(function(){var c=a(this),d=c.data("item"),f=b.options.itemValue(d),g=b.options.itemText(d),h=b.options.tagClass(d);if(c.attr("class",null),c.addClass("tag "+e(h)),c.contents().filter(function(){return 3==this.nodeType})[0].nodeValue=e(g),b.isSelect){var i=a("option",b.$element).filter(function(){return a(this).data("item")===d});i.attr("value",f)}})},items:function(){return this.itemsArray},pushVal:function(){var b=this,c=a.map(b.items(),function(a){return b.options.itemValue(a).toString()});b.$element.val(c,!0).trigger("change")},build:function(b){var e=this;if(e.options=a.extend({},h,b),e.objectItems&&(e.options.freeInput=!1),c(e.options,"itemValue"),c(e.options,"itemText"),d(e.options,"tagClass"),e.options.typeahead){var i=e.options.typeahead||{};d(i,"source"),e.$input.typeahead(a.extend({},i,{source:function(b,c){function d(a){for(var b=[],d=0;d$1")}}))}if(e.options.typeaheadjs){var j=e.options.typeaheadjs||{};e.$input.typeahead(null,j).on("typeahead:selected",a.proxy(function(a,b){e.add(j.valueKey?b[j.valueKey]:b),e.$input.typeahead("val","")},e))}e.$container.on("click",a.proxy(function(){e.$element.attr("disabled")||e.$input.removeAttr("disabled"),e.$input.focus()},e)),e.options.addOnBlur&&e.options.freeInput&&e.$input.on("focusout",a.proxy(function(){0===a(".typeahead, .twitter-typeahead",e.$container).length&&(e.add(e.$input.val()),e.$input.val(""))},e)),e.$container.on("keydown","input",a.proxy(function(b){var c=a(b.target),d=e.findInputWrapper();if(e.$element.attr("disabled"))return void e.$input.attr("disabled","disabled");switch(b.which){case 8:if(0===f(c[0])){var g=d.prev();g&&e.remove(g.data("item"))}break;case 46:if(0===f(c[0])){var h=d.next();h&&e.remove(h.data("item"))}break;case 37:var i=d.prev();0===c.val().length&&i[0]&&(i.before(d),c.focus());break;case 39:var j=d.next();0===c.val().length&&j[0]&&(j.after(d),c.focus())}{var k=c.val().length;Math.ceil(k/5)}c.attr("size",Math.max(this.inputSize,c.val().length))},e)),e.$container.on("keypress","input",a.proxy(function(b){var c=a(b.target);if(e.$element.attr("disabled"))return void e.$input.attr("disabled","disabled");var d=c.val(),f=e.options.maxChars&&d.length>=e.options.maxChars;e.options.freeInput&&(g(b,e.options.confirmKeys)||f)&&(e.add(f?d.substr(0,e.options.maxChars):d),c.val(""),b.preventDefault());{var h=c.val().length;Math.ceil(h/5)}c.attr("size",Math.max(this.inputSize,c.val().length))},e)),e.$container.on("click","[data-role=remove]",a.proxy(function(b){e.$element.attr("disabled")||e.remove(a(b.target).closest(".tag").data("item"))},e)),e.options.itemValue===h.itemValue&&("INPUT"===e.$element[0].tagName?e.add(e.$element.val()):a("option",e.$element).each(function(){e.add(a(this).attr("value"),!0)}))},destroy:function(){var a=this;a.$container.off("keypress","input"),a.$container.off("click","[role=remove]"),a.$container.remove(),a.$element.removeData("tagsinput"),a.$element.show()},focus:function(){this.$input.focus()},input:function(){return this.$input},findInputWrapper:function(){for(var b=this.$input[0],c=this.$container[0];b&&b.parentNode!==c;)b=b.parentNode;return a(b)}},a.fn.tagsinput=function(c,d){var e=[];return this.each(function(){var f=a(this).data("tagsinput");if(f)if(c||d){if(void 0!==f[c]){var g=f[c](d);void 0!==g&&e.push(g)}}else e.push(f);else f=new b(this,c),a(this).data("tagsinput",f),e.push(f),"SELECT"===this.tagName&&a("option",a(this)).attr("selected","selected"),a(this).val(a(this).val())}),"string"==typeof c?e.length>1?e:e[0]:e},a.fn.tagsinput.Constructor=b;var i=a("
");a(function(){a("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput()})}(window.jQuery); +//# sourceMappingURL=bootstrap-tagsinput.min.js.map \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/js/bootstrap.min.js b/packaging/src/emc-metalnx-shared/src/main/resources/static/js/bootstrap.min.js new file mode 100644 index 000000000..7c1561a8b --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/js/bootstrap.min.js @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.2.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.2.0",d.prototype.close=function(b){function c(){f.detach().trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",c).emulateTransitionEnd(150):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.2.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),d[e](null==f[b]?this.options[b]:f[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b).on("keydown.bs.carousel",a.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.2.0",c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.to=function(b){var c=this,d=this.getItemIndex(this.$active=this.$element.find(".item.active"));return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=e[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:g});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,f&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(e)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:g});return a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one("bsTransitionEnd",function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger(m)),f&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(b=!b),e||d.data("bs.collapse",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};c.VERSION="3.2.0",c.DEFAULTS={toggle:!0},c.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},c.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var c=a.Event("show.bs.collapse");if(this.$element.trigger(c),!c.isDefaultPrevented()){var d=this.$parent&&this.$parent.find("> .panel > .in");if(d&&d.length){var e=d.data("bs.collapse");if(e&&e.transitioning)return;b.call(d,"hide"),e||d.data("bs.collapse",null)}var f=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[f](0),this.transitioning=1;var g=function(){this.$element.removeClass("collapsing").addClass("collapse in")[f](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return g.call(this);var h=a.camelCase(["scroll",f].join("-"));this.$element.one("bsTransitionEnd",a.proxy(g,this)).emulateTransitionEnd(350)[f](this.$element[0][h])}}},c.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},c.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var d=a.fn.collapse;a.fn.collapse=b,a.fn.collapse.Constructor=c,a.fn.collapse.noConflict=function(){return a.fn.collapse=d,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(c){var d,e=a(this),f=e.attr("data-target")||c.preventDefault()||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),g=a(f),h=g.data("bs.collapse"),i=h?"toggle":e.data(),j=e.attr("data-parent"),k=j&&a(j);h&&h.transitioning||(k&&k.find('[data-toggle="collapse"][data-parent="'+j+'"]').not(e).addClass("collapsed"),e[g.hasClass("in")?"addClass":"removeClass"]("collapsed")),b.call(g,i)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.2.0",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('' + + '
' + + '
' + + '
'+ + '
'+ + '
' + + '
'+ + '
'+ + '
' + ); + + $("#"+table_id+"_length").addClass("pull-right"); +} + +//Adds delete all tickets button on tickets table +function addDeleteTickets(){ + $("div.toolbar").html( + ' ' + ); +} + +//Adds Action button on Collection table +function addCollectionMetadataDelBtn(table_id, datatable){ + $("div.toolbar").html( + '' + + ' ' + + '' + ); +} + +//Adds Action button on Template table +function addTemplateActionBtn(table_id, datatable){ + $('div.toolbar').html( + '
'+ + '' + + ' ' + + '
' + ); +} + +/** + * Resets the start page of a table. + * After any file operation (copy, move, delete, etc), the table needs to go back to the first page. + * */ +function resetDataTablesStart () { + for(var i = 0; i < localStorage.length; i++) { + var key = localStorage.key(i); + if(key.indexOf('emc-metalnx-web') > -1) { + var data = JSON.parse(localStorage.getItem(key)); + data.start = 0; + localStorage.setItem(key, JSON.stringify(data)); + } + } +} + +$.fn.dataTable.ext.errMode = function(xhr, textStatus) { + location.reload(); +}; \ No newline at end of file diff --git a/packaging/src/emc-metalnx-shared/src/main/resources/static/js/dataTables.bootstrap.min.js b/packaging/src/emc-metalnx-shared/src/main/resources/static/js/dataTables.bootstrap.min.js new file mode 100644 index 000000000..07f2175b5 --- /dev/null +++ b/packaging/src/emc-metalnx-shared/src/main/resources/static/js/dataTables.bootstrap.min.js @@ -0,0 +1,8 @@ +/*! + DataTables Bootstrap 3 integration + ©2011-2014 SpryMedia Ltd - datatables.net/license +*/ +(function(l,q){var d=function(b,c){b.extend(!0,c.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-5'i><'col-sm-7'p>>",renderer:"bootstrap"});b.extend(c.ext.classes,{sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm"});c.ext.renderer.pageButton.bootstrap=function(g,d,r,s,i,m){var t=new c.Api(g),u=g.oClasses,j=g.oLanguage.oPaginate,e,f,n=0,p=function(c,d){var k,h,o,a,l=function(a){a.preventDefault(); +b(a.currentTarget).hasClass("disabled")||t.page(a.data.action).draw("page")};k=0;for(h=d.length;k",{"class":u.sPageButton+ +" "+f,id:0===r&&"string"===typeof a?g.sTableId+"_"+a:null}).append(b("",{href:"#","aria-controls":g.sTableId,"data-dt-idx":n,tabindex:g.iTabIndex}).html(e)).appendTo(c),g.oApi._fnBindAction(o,{action:a},l),n++)}},h;try{h=b(d).find(q.activeElement).data("dt-idx")}catch(l){}p(b(d).empty().html('