From 59650a2878168baf045ae9ae3e5556e30cb2cae1 Mon Sep 17 00:00:00 2001 From: Fabio Buso Date: Mon, 10 Oct 2022 10:10:19 +0200 Subject: [PATCH] [HOPSWORKS-2670] remove dela (#1119) Co-authored-by: Alex Ormenisan --- hopsworks-IT/src/test/ruby/spec/dela_spec.rb | 83 --- hopsworks-UT/pom.xml | 7 +- hopsworks-api/pom.xml | 6 - .../api/dela/DelaClusterService.java | 95 --- .../api/dela/DelaProjectService.java | 341 --------- .../hops/hopsworks/api/dela/DelaService.java | 281 -------- .../hopsworks/api/dela/RemoteDelaService.java | 107 --- .../api/dela/UserContentsSummaryJSON.java | 87 --- .../hopsworks/api/dela/dto/BootstrapDTO.java | 57 -- .../hopsworks/api/dela/dto/DelaClientDTO.java | 63 -- .../hopsworks/api/dela/dto/InodeIdDTO.java | 55 -- .../api/hopssite/CommentService.java | 189 ----- .../api/hopssite/HopssiteService.java | 252 ------- .../hopsworks/api/hopssite/RatingService.java | 145 ---- .../api/hopssite/dto/CategoryDTO.java | 90 --- .../api/hopssite/dto/CommentIssueReqDTO.java | 69 -- .../api/hopssite/dto/CommentReqDTO.java | 69 -- .../api/hopssite/dto/DatasetIssueReqDTO.java | 68 -- .../api/hopssite/dto/DatasetReqDTO.java | 77 --- .../hopssite/dto/HopsSiteServiceInfoDTO.java | 89 --- .../api/hopssite/dto/LocalDatasetDTO.java | 137 ---- .../api/hopssite/dto/LocalDatasetHelper.java | 69 -- .../api/hopssite/dto/RateReqDTO.java | 79 --- .../api/hopssite/dto/RatingValueDTO.java | 55 -- .../hopsworks/api/project/ProjectService.java | 11 +- .../hops/hopsworks/api/util/VersionsDTO.java | 1 - .../application/config/ApplicationConfig.java | 9 - .../hopsworks/ca/api/ApplicationConfig.java | 1 - .../certificates/CertificatesResource.java | 7 - .../DelaTrackerCertsResource.java | 126 ---- .../ca/controllers/CertificateType.java | 3 +- .../io/hops/hopsworks/ca/controllers/PKI.java | 1 - .../hopsworks/ca/controllers/PKIUtils.java | 3 +- hopsworks-cluster/pom.xml | 145 ---- .../io/hops/hopsworks/cluster/Cluster.java | 207 ------ .../io/hops/hopsworks/cluster/ClusterDTO.java | 110 --- .../cluster/ClusterJsonResponse.java | 49 -- .../hops/hopsworks/cluster/ClusterState.java | 56 -- .../hops/hopsworks/cluster/ClusterYmlDTO.java | 129 ---- .../hopsworks/cluster/controller/Cleanup.java | 86 --- .../cluster/controller/ClusterController.java | 504 -------------- .../controller/DelaTrackerCertController.java | 144 ---- .../exception/mapper/ClusterJsonResponse.java | 24 - .../mapper/ClusterThrowableMapper.java | 42 -- .../cluster/response/filter/CORSFilter.java | 60 -- .../application/config/ApplicationConfig.java | 63 -- hopsworks-cluster/src/main/webapp/.bowerrc | 4 - .../src/main/webapp/WEB-INF/glassfish-web.xml | 27 - .../src/main/webapp/WEB-INF/web.xml | 89 --- hopsworks-cluster/src/main/webapp/bower.json | 30 - .../src/main/webapp/css/main.css | 56 -- .../src/main/webapp/img/network.png | Bin 286103 -> 0 bytes hopsworks-cluster/src/main/webapp/index.html | 64 -- hopsworks-cluster/src/main/webapp/js/app.js | 78 --- .../src/main/webapp/js/controllers.js | 212 ------ .../src/main/webapp/js/directives.js | 80 --- .../src/main/webapp/js/services.js | 127 ---- hopsworks-cluster/src/main/webapp/js/util.js | 70 -- .../src/main/webapp/package.json | 38 - .../src/main/webapp/partials/home.html | 55 -- .../src/main/webapp/partials/register.html | 320 --------- .../main/webapp/partials/registerCluster.html | 305 -------- .../src/main/webapp/partials/unregister.html | 119 ---- .../partials/viewRegisteredClusters.html | 128 ---- .../src/test/javascript/karma.conf.js | 71 -- .../hopsworks/common/dela/AddressJSON.java | 99 --- .../hopsworks/common/dela/DelaClientType.java | 66 -- .../hopsworks/common/proxies/CAProxy.java | 11 +- .../security/CertificatesController.java | 18 +- .../DelaCertsMasterPasswordHandler.java | 125 ---- .../common/util/LocalhostServices.java | 110 --- .../hops/hopsworks/common/util/Settings.java | 282 +------- hopsworks-dela/pom.xml | 99 --- .../hopsworks/dela/DelaDatasetController.java | 189 ----- .../hopsworks/dela/DelaHdfsController.java | 330 --------- .../hops/hopsworks/dela/DelaSetupWorker.java | 452 ------------ .../hopsworks/dela/DelaStateController.java | 146 ---- .../hopsworks/dela/DelaWorkerController.java | 239 ------- .../hopsworks/dela/RemoteDelaController.java | 94 --- .../io/hops/hopsworks/dela/TransferDela.java | 44 -- .../dela/TransferDelaController.java | 261 ------- .../cluster/ClusterDatasetController.java | 65 -- .../dela/dto/common/ClusterAddressDTO.java | 88 --- .../hopsworks/dela/dto/common/UserDTO.java | 138 ---- .../dela/dto/hopssite/ClusterServiceDTO.java | 137 ---- .../dela/dto/hopssite/CommentDTO.java | 129 ---- .../dela/dto/hopssite/CommentIssueDTO.java | 83 --- .../dela/dto/hopssite/DatasetDTO.java | 355 ---------- .../dela/dto/hopssite/HopsSiteDatasetDTO.java | 141 ---- .../hopsworks/dela/dto/hopssite/RateDTO.java | 73 -- .../dela/dto/hopssite/RatingDTO.java | 73 -- .../dela/dto/hopssite/SearchServiceDTO.java | 169 ----- .../dto/hopsworks/HopsworksSearchDTO.java | 144 ---- .../dto/hopsworks/HopsworksTransferDTO.java | 102 --- .../hopsworks/dela/hopssite/HopsSite.java | 182 ----- .../dela/hopssite/HopsSiteExceptions.java | 51 -- .../dela/hopssite/HopssiteController.java | 650 ------------------ .../dela/old_dto/ElementSummaryJSON.java | 89 --- .../hopsworks/dela/old_dto/ErrorDescJSON.java | 63 -- .../dela/old_dto/ExtendedDetails.java | 72 -- .../hops/hopsworks/dela/old_dto/FileInfo.java | 85 --- .../hopsworks/dela/old_dto/HDFSEndpoint.java | 79 --- .../hopsworks/dela/old_dto/HDFSResource.java | 79 --- .../hopsworks/dela/old_dto/HdfsDetails.java | 72 -- .../dela/old_dto/HopsContentsReqJSON.java | 62 -- .../dela/old_dto/HopsContentsSummaryJSON.java | 113 --- .../dela/old_dto/HopsDatasetDetailsDTO.java | 83 --- .../hopsworks/dela/old_dto/HopsResource.java | 53 -- .../old_dto/HopsTorrentAdvanceDownload.java | 96 --- .../old_dto/HopsTorrentStartDownload.java | 126 ---- .../dela/old_dto/HopsTorrentUpload.java | 124 ---- .../hopsworks/dela/old_dto/KafkaDetails.java | 73 -- .../hopsworks/dela/old_dto/KafkaEndpoint.java | 115 ---- .../hopsworks/dela/old_dto/KafkaResource.java | 72 -- .../hopsworks/dela/old_dto/ManifestJSON.java | 133 ---- .../dela/old_dto/RemoveGVodJSON.java | 64 -- .../dela/old_dto/RemoveTorrentDTO.java | 60 -- .../dela/old_dto/StatusGVoDJSON.java | 73 -- .../hopsworks/dela/old_dto/SuccessJSON.java | 64 -- .../old_dto/TorrentExtendedStatusJSON.java | 96 --- .../hopsworks/dela/old_dto/TorrentId.java | 69 -- .../old_hopssite_dto/DatasetIssueDTO.java | 95 --- .../old_hopssite_dto/PopularDatasetJSON.java | 99 --- .../dela/old_hopssite_dto/RateIdDTO.java | 62 -- .../hopsworks/dela/util/ManifestHelper.java | 76 -- .../hopsworks/util/CertificateHelper.java | 212 ------ .../hops/hopsworks/util/SettingsHelper.java | 114 --- .../src/main/resources/META-INF/MANIFEST.MF | 2 - hopsworks-ear/pom.xml | 23 +- .../exceptions/DelaCSRCheckException.java | 41 -- .../hopsworks/exceptions/DelaException.java | 91 --- .../hops/hopsworks/restutils/RESTCodes.java | 61 -- pom.xml | 35 - 133 files changed, 14 insertions(+), 14281 deletions(-) delete mode 100644 hopsworks-IT/src/test/ruby/spec/dela_spec.rb delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaClusterService.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaProjectService.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaService.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/RemoteDelaService.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/UserContentsSummaryJSON.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/BootstrapDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/DelaClientDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/InodeIdDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/CommentService.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/HopssiteService.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/RatingService.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CategoryDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CommentIssueReqDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CommentReqDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/DatasetIssueReqDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/DatasetReqDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/HopsSiteServiceInfoDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/LocalDatasetDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/LocalDatasetHelper.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/RateReqDTO.java delete mode 100644 hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/RatingValueDTO.java delete mode 100644 hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/DelaTrackerCertsResource.java delete mode 100644 hopsworks-cluster/pom.xml delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/Cluster.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterDTO.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterJsonResponse.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterState.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterYmlDTO.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/Cleanup.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/ClusterController.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/DelaTrackerCertController.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/exception/mapper/ClusterJsonResponse.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/exception/mapper/ClusterThrowableMapper.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/response/filter/CORSFilter.java delete mode 100644 hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/rest/application/config/ApplicationConfig.java delete mode 100644 hopsworks-cluster/src/main/webapp/.bowerrc delete mode 100644 hopsworks-cluster/src/main/webapp/WEB-INF/glassfish-web.xml delete mode 100644 hopsworks-cluster/src/main/webapp/WEB-INF/web.xml delete mode 100644 hopsworks-cluster/src/main/webapp/bower.json delete mode 100644 hopsworks-cluster/src/main/webapp/css/main.css delete mode 100644 hopsworks-cluster/src/main/webapp/img/network.png delete mode 100644 hopsworks-cluster/src/main/webapp/index.html delete mode 100644 hopsworks-cluster/src/main/webapp/js/app.js delete mode 100644 hopsworks-cluster/src/main/webapp/js/controllers.js delete mode 100644 hopsworks-cluster/src/main/webapp/js/directives.js delete mode 100644 hopsworks-cluster/src/main/webapp/js/services.js delete mode 100644 hopsworks-cluster/src/main/webapp/js/util.js delete mode 100644 hopsworks-cluster/src/main/webapp/package.json delete mode 100644 hopsworks-cluster/src/main/webapp/partials/home.html delete mode 100644 hopsworks-cluster/src/main/webapp/partials/register.html delete mode 100644 hopsworks-cluster/src/main/webapp/partials/registerCluster.html delete mode 100644 hopsworks-cluster/src/main/webapp/partials/unregister.html delete mode 100644 hopsworks-cluster/src/main/webapp/partials/viewRegisteredClusters.html delete mode 100644 hopsworks-cluster/src/test/javascript/karma.conf.js delete mode 100644 hopsworks-common/src/main/java/io/hops/hopsworks/common/dela/AddressJSON.java delete mode 100644 hopsworks-common/src/main/java/io/hops/hopsworks/common/dela/DelaClientType.java delete mode 100644 hopsworks-common/src/main/java/io/hops/hopsworks/common/security/DelaCertsMasterPasswordHandler.java delete mode 100644 hopsworks-common/src/main/java/io/hops/hopsworks/common/util/LocalhostServices.java delete mode 100644 hopsworks-dela/pom.xml delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaDatasetController.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaHdfsController.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaSetupWorker.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaStateController.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaWorkerController.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/RemoteDelaController.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/TransferDela.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/TransferDelaController.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/cluster/ClusterDatasetController.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/common/ClusterAddressDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/common/UserDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/ClusterServiceDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/CommentDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/CommentIssueDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/DatasetDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/HopsSiteDatasetDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/RateDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/RatingDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/SearchServiceDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopsworks/HopsworksSearchDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopsworks/HopsworksTransferDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopsSite.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopsSiteExceptions.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopssiteController.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ElementSummaryJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ErrorDescJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ExtendedDetails.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/FileInfo.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HDFSEndpoint.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HDFSResource.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HdfsDetails.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsContentsReqJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsContentsSummaryJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsDatasetDetailsDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsResource.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentAdvanceDownload.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentStartDownload.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentUpload.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaDetails.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaEndpoint.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaResource.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ManifestJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/RemoveGVodJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/RemoveTorrentDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/StatusGVoDJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/SuccessJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/TorrentExtendedStatusJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/TorrentId.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/DatasetIssueDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/PopularDatasetJSON.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/RateIdDTO.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/dela/util/ManifestHelper.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/util/CertificateHelper.java delete mode 100644 hopsworks-dela/src/main/java/io/hops/hopsworks/util/SettingsHelper.java delete mode 100644 hopsworks-dela/src/main/resources/META-INF/MANIFEST.MF delete mode 100644 hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/DelaCSRCheckException.java delete mode 100644 hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/DelaException.java diff --git a/hopsworks-IT/src/test/ruby/spec/dela_spec.rb b/hopsworks-IT/src/test/ruby/spec/dela_spec.rb deleted file mode 100644 index 71a8ac669d..0000000000 --- a/hopsworks-IT/src/test/ruby/spec/dela_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -=begin - This file is part of Hopsworks - Copyright (C) 2018, Logical Clocks AB. All rights reserved - - Hopsworks is free software: you can redistribute it and/or modify it under the terms of - the GNU Affero General Public License as published by the Free Software Foundation, - either version 3 of the License, or (at your option) any later version. - - Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with this program. - If not, see . -=end - -describe "On #{ENV['OS']}" do - after(:all) {clean_all_test_projects(spec: "dela")} - describe 'Dela' do - before :all do - @debugOpt = false - end - - # describe 'tracker certs' do - # before :all do - # @certs_dir = Variables.find_by(id: "certs_dir").value - # @subject = "/C=SE/ST=Stockholm/L=SE/O=SE/OU=1/CN=test/emailAddress=agent@hops.io" - # reset_session - # with_cluster_agent_session - # - # # Remove entries from previous executions - # ClusterCert.destroy_all - # end - # - # it 'should fail to sign the certificate with empty csr' do - # post "#{ENV['HOPSWORKS_DELA_TRACKER']}/cluster/certificate", {} - # expect_status_details(422) - # end - # - # it 'should fail to sign the certificate with no db entry in the db' do - # post "#{ENV['HOPSWORKS_DELA_TRACKER']}/cluster/certificate", {csr: generate_csr(@subject)} - # expect_status_details(400) - # end - # - # it 'should sign the dela certificate with entry in the db', vm: true do - # # Add entry in the database - # # `id` int(11) NOT NULL AUTO_INCREMENT, - # # `agent_id` int(11) NOT NULL, - # # `common_name` varchar(64) COLLATE latin1_general_cs NOT NULL, - # # `organization_name` varchar(64) COLLATE latin1_general_cs NOT NULL, - # # `organizational_unit_name` varchar(64) COLLATE latin1_general_cs NOT NULL, - # # `serial_number` varchar(45) COLLATE latin1_general_cs DEFAULT NULL, - # # `registration_status` varchar(45) COLLATE latin1_general_cs NOT NULL, - # # `validation_key` varchar(128) COLLATE latin1_general_cs DEFAULT NULL, - # # `validation_key_date` timestamp NULL DEFAULT NULL, - # # `registration_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - # # - # # "/C=SE/ST=Stockholm/L=SE/OU=1/O=SE/CN=test/emailAddress=agent@hops.io" - # ClusterCert.create(agent_id: 10001, - # common_name: "test", - # organization_name: "SE", - # organizational_unit_name: "1", - # registration_status: "Registered") - # post "#{ENV['HOPSWORKS_DELA_TRACKER']}/cluster/certificate", {csr: generate_csr(@subject)} - # expect_status_details(200) - # - # # Check that the certificate is on the local fs. this assumes you are running the - # # tests on a proper vm - # check_certificate_exists(@certs_dir + "/intermediate/", "test", @subject) - # end - # - # it 'should fail to sign twice a certificate for the same cluster' do - # post "#{ENV['HOPSWORKS_DELA_TRACKER']}/cluster/certificate", {csr: generate_csr(@subject)} - # expect_status_details(400) - # end - # - # it 'should revoke the certificate' do - # delete "#{ENV['HOPSWORKS_DELA_TRACKER']}/cluster/certificate?certId=test" - # expect_status_details(200) - # end - # end - end -end diff --git a/hopsworks-UT/pom.xml b/hopsworks-UT/pom.xml index f9e738b097..d533fadeea 100644 --- a/hopsworks-UT/pom.xml +++ b/hopsworks-UT/pom.xml @@ -48,11 +48,6 @@ ejb - - io.hops.hopsworks - hopsworks-dela - ejb - io.hops.hopsworks hopsworks-rest-utils @@ -128,4 +123,4 @@ - \ No newline at end of file + diff --git a/hopsworks-api/pom.xml b/hopsworks-api/pom.xml index 594b1006a7..5971ebd387 100644 --- a/hopsworks-api/pom.xml +++ b/hopsworks-api/pom.xml @@ -76,12 +76,6 @@ ejb provided - - io.hops.hopsworks - hopsworks-dela - ejb - provided - io.hops.hopsworks hopsworks-jwt diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaClusterService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaClusterService.java deleted file mode 100644 index 8bb088757f..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaClusterService.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.dela; - -import io.hops.hopsworks.api.filter.Audience; -import io.hops.hopsworks.api.filter.NoCacheResponse; -import io.hops.hopsworks.common.dao.dataset.DataSetDTO; -import io.hops.hopsworks.dela.cluster.ClusterDatasetController; -import io.hops.hopsworks.jwt.annotation.JWTRequired; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import io.swagger.annotations.Api; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.util.LinkedList; -import java.util.List; -import java.util.logging.Logger; - -@Path("/delacluster") -@Stateless -@JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@Api(value = "Dela Cluster Service", - description = "Dela Cluster Service") -@TransactionAttribute(TransactionAttributeType.NEVER) -public class DelaClusterService { - - private static final Logger LOGGER = Logger.getLogger(DelaClusterService.class.getName()); - @EJB - private NoCacheResponse noCacheResponse; - @EJB - private ClusterDatasetController clusterDatasetCtrl; - - @GET - @Produces(MediaType.APPLICATION_JSON) - public Response getPublicDatasets(@Context SecurityContext sc) { - List clusterDatasets = clusterDatasetCtrl.getPublicDatasets(); - List localDS = new LinkedList<>(); - for(Dataset ds : clusterDatasets) { - localDS.add(new DataSetDTO(ds)); - } - GenericEntity> datasets = new GenericEntity>(localDS) { - }; - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(datasets).build(); - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaProjectService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaProjectService.java deleted file mode 100644 index 6799ec5d83..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaProjectService.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.dela; - -import com.logicalclocks.servicediscoverclient.exceptions.ServiceDiscoveryException; -import io.hops.hopsworks.api.dela.dto.InodeIdDTO; -import io.hops.hopsworks.api.filter.AllowedProjectRoles; -import io.hops.hopsworks.api.filter.Audience; -import io.hops.hopsworks.api.filter.NoCacheResponse; -import io.hops.hopsworks.api.jwt.JWTHelper; -import io.hops.hopsworks.api.util.RESTApiJsonResponse; -import io.hops.hopsworks.common.dao.dataset.DatasetFacade; -import io.hops.hopsworks.common.dao.hdfs.inode.InodeFacade; -import io.hops.hopsworks.common.dao.project.ProjectFacade; -import io.hops.hopsworks.common.dao.user.UserFacade; -import io.hops.hopsworks.common.hosts.ServiceDiscoveryController; -import io.hops.hopsworks.common.kafka.KafkaBrokers; -import io.hops.hopsworks.common.kafka.KafkaController; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.DelaHdfsController; -import io.hops.hopsworks.dela.DelaWorkerController; -import io.hops.hopsworks.dela.TransferDelaController; -import io.hops.hopsworks.dela.dto.hopsworks.HopsworksTransferDTO; -import io.hops.hopsworks.dela.old_dto.ElementSummaryJSON; -import io.hops.hopsworks.dela.old_dto.HopsContentsSummaryJSON; -import io.hops.hopsworks.dela.old_dto.KafkaEndpoint; -import io.hops.hopsworks.dela.old_dto.ManifestJSON; -import io.hops.hopsworks.dela.old_dto.SuccessJSON; -import io.hops.hopsworks.dela.old_dto.TorrentExtendedStatusJSON; -import io.hops.hopsworks.dela.old_dto.TorrentId; -import io.hops.hopsworks.exceptions.DatasetException; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.exceptions.ProvenanceException; -import io.hops.hopsworks.exceptions.ServiceException; -import io.hops.hopsworks.jwt.annotation.JWTRequired; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import io.hops.hopsworks.persistence.entity.hdfs.inode.Inode; -import io.hops.hopsworks.persistence.entity.project.Project; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.restutils.RESTCodes; -import io.swagger.annotations.ApiParam; - -import javax.ejb.EJB; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.enterprise.context.RequestScoped; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; - -@RequestScoped -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@TransactionAttribute(TransactionAttributeType.NEVER) -public class DelaProjectService { - - private static final Logger LOGGER = Logger.getLogger(DelaProjectService.class.getName()); - @EJB - private NoCacheResponse noCacheResponse; - @EJB - private Settings settings; - @EJB - private DelaWorkerController delaWorkerCtrl; - @EJB - private TransferDelaController delaTransferCtrl; - @EJB - private DelaHdfsController delaHdfsCtrl; - @EJB - private KafkaController kafkaController; - @EJB - private UserFacade userFacade; - @EJB - private DatasetFacade datasetFacade; - @EJB - private ProjectFacade projectFacade; - @EJB - private InodeFacade inodeFacade; - @EJB - private JWTHelper jWTHelper; - @EJB - private KafkaBrokers kafkaBrokers; - @EJB - private ServiceDiscoveryController serviceDiscoveryController; - - private Project project; - private Integer projectId; - - public void setProjectId(Integer projectId) { - this.projectId = projectId; - this.project = projectFacade.find(projectId); - } - - public Integer getProjectId() { - return projectId; - } - - private Response successResponse(Object content) { - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(content).build(); - } - - @GET - @Path("/transfers") - @Produces(MediaType.APPLICATION_JSON) - @AllowedProjectRoles({AllowedProjectRoles.DATA_SCIENTIST, AllowedProjectRoles.DATA_OWNER}) - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response getProjectContents(@Context SecurityContext sc) throws DelaException { - - List projectIds = new LinkedList<>(); - projectIds.add(projectId); - - HopsContentsSummaryJSON.Contents resp = delaTransferCtrl.getContents(projectIds); - ElementSummaryJSON[] projectContents = resp.getContents().get(projectId); - if (projectContents == null) { - projectContents = new ElementSummaryJSON[0]; - } - return successResponse(projectContents); - } - - @POST - @Path("/uploads") - @Produces(MediaType.APPLICATION_JSON) - @AllowedProjectRoles({AllowedProjectRoles.DATA_OWNER}) - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response publish(@Context SecurityContext sc, InodeIdDTO inodeId) throws DelaException { - Inode inode = getInode(inodeId.getId()); - Dataset dataset = getDatasetByInode(inode); - Users user = jWTHelper.getUserPrincipal(sc); - delaWorkerCtrl.shareDatasetWithHops(project, dataset, user); - RESTApiJsonResponse json = new RESTApiJsonResponse(); - json.setSuccessMessage("Dataset transfer is started - published"); - return successResponse(json); - } - - @GET - @Path("/transfers/{publicDSId}") - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @AllowedProjectRoles({AllowedProjectRoles.DATA_SCIENTIST, AllowedProjectRoles.DATA_OWNER}) - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response getExtendedDetails(@PathParam("publicDSId") String publicDSId, @Context SecurityContext sc) - throws DelaException { - TorrentId torrentId = new TorrentId(publicDSId); - TorrentExtendedStatusJSON resp = delaTransferCtrl.details(torrentId); - return successResponse(resp); - } - - @POST - @Path("/transfers/{publicDSId}/cancel") - @Produces(MediaType.APPLICATION_JSON) - @AllowedProjectRoles({AllowedProjectRoles.DATA_OWNER}) - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response removePublic(@Context SecurityContext sc, @PathParam("publicDSId") String publicDSId, - @ApiParam(value="delete dataset", required = true) @QueryParam("clean") boolean clean) - throws DelaException, IOException, DatasetException { - - Dataset dataset = getDatasetByPublicId(publicDSId); - Users user = jWTHelper.getUserPrincipal(sc); - if (clean) { - delaWorkerCtrl.unshareFromHopsAndClean(project, dataset, user); - } else { - delaWorkerCtrl.unshareFromHops(project, dataset, user); - } - RESTApiJsonResponse json = new RESTApiJsonResponse(); - json.setSuccessMessage("Dataset is now private"); - return successResponse(json); - } - - @POST - @Path("/downloads/{publicDSId}/manifest") - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @AllowedProjectRoles({AllowedProjectRoles.DATA_SCIENTIST, AllowedProjectRoles.DATA_OWNER}) - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response startDownload(@Context SecurityContext sc, @PathParam("publicDSId") String publicDSId, - HopsworksTransferDTO.Download downloadDTO) throws DelaException, DatasetException, ProvenanceException, - IOException { - Users user = jWTHelper.getUserPrincipal(sc); - //dataset not createed yet - - ManifestJSON manifest = delaWorkerCtrl.startDownload(project, user, downloadDTO); - return successResponse(manifest); - } - - @POST - @Path("/downloads/{publicDSId}/hdfs") - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @AllowedProjectRoles({AllowedProjectRoles.DATA_SCIENTIST, AllowedProjectRoles.DATA_OWNER}) - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response downloadDatasetHdfs(@Context SecurityContext sc, @PathParam("publicDSId") String publicDSId, - HopsworksTransferDTO.Download downloadDTO) throws DelaException { - Users user = jWTHelper.getUserPrincipal(sc); - Dataset dataset = getDatasetByPublicId(downloadDTO.getPublicDSId()); - - delaWorkerCtrl.advanceDownload(project, dataset, user, downloadDTO, null, null); - return successResponse(new SuccessJSON("")); - } - - @POST - @Path("/downloads/{publicDSId}/kafka") - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @AllowedProjectRoles({AllowedProjectRoles.DATA_SCIENTIST, AllowedProjectRoles.DATA_OWNER}) - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response downloadDatasetKafka(@Context HttpServletRequest req, @Context SecurityContext sc, - @PathParam("publicDSId") String publicDSId, HopsworksTransferDTO.Download downloadDTO) - throws DelaException, ServiceException { - Users user = jWTHelper.getUserPrincipal(sc); - Dataset dataset = getDatasetByPublicId(publicDSId); - - String certPath = kafkaController.getKafkaCertPaths(project); - Optional kafkaBrokerOpt = kafkaBrokers.getAnyKafkaBroker(); - String brokerEndpoint = kafkaBrokerOpt.orElseThrow(() -> - new DelaException(RESTCodes.DelaErrorCode.MISCONFIGURED, Level.WARNING, DelaException.Source.KAFKA, - "Could not find any active Kafka broker")); - - String restEndpoint; - try { - restEndpoint = "https://" + serviceDiscoveryController - .constructServiceFQDNWithPort(ServiceDiscoveryController.HopsworksService.HOPSWORKS_APP); - } catch (ServiceDiscoveryException e) { - throw new ServiceException(RESTCodes.ServiceErrorCode.SERVICE_NOT_FOUND, Level.SEVERE, - "Could not find Hopsworks service"); - } - - String keyStore = certPath + "/keystore.jks"; - String trustStore = certPath + "/truststore.jks"; - KafkaEndpoint kafkaEndpoint = new KafkaEndpoint(brokerEndpoint, restEndpoint, settings.getDELA_DOMAIN(), - "" + project.getId(), keyStore, trustStore); - - delaWorkerCtrl.advanceDownload(project, dataset, user, downloadDTO, req.getSession().getId(), - kafkaEndpoint); - return successResponse(new SuccessJSON("")); - } - - @GET - @Path("/transfers/{publicDSId}/manifest/") - @Produces(MediaType.APPLICATION_JSON) - @AllowedProjectRoles({AllowedProjectRoles.DATA_OWNER}) - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response showManifest(@Context SecurityContext sc, @PathParam("publicDSId") String publicDSId) - throws DelaException { - Dataset dataset = getDatasetByPublicId(publicDSId); - Users user = jWTHelper.getUserPrincipal(sc); - if (!dataset.isPublicDs()) { - throw new DelaException(RESTCodes.DelaErrorCode.DATASET_NOT_PUBLIC, Level.FINE, DelaException.Source.LOCAL); - } - - ManifestJSON manifestJSON = delaHdfsCtrl.readManifest(project, dataset, user); - return successResponse(manifestJSON); - } - - private Users getUser(String email) throws DelaException { - Users user = userFacade.findByEmail(email); - if (user == null) { - throw new DelaException(RESTCodes.DelaErrorCode.USER_NOT_FOUND, Level.FINE, DelaException.Source.LOCAL); - } - return user; - } - - private Dataset getDatasetByPublicId(String publicDSId) throws DelaException { - Optional d = datasetFacade.findByPublicDsIdProject(publicDSId, project); - if (!d.isPresent()) { - throw new DelaException(RESTCodes.DelaErrorCode.DATASET_DOES_NOT_EXIST, Level.FINE, DelaException.Source.MYSQL); - } - return d.get(); - } - - private Dataset getDatasetByInode(Inode inode) throws DelaException { - Dataset dataset = datasetFacade.findByProjectAndInode(this.project, inode); - if (dataset == null) { - throw new DelaException(RESTCodes.DelaErrorCode.DATASET_DOES_NOT_EXIST, Level.FINE, DelaException.Source.LOCAL); - } - return dataset; - } - - private Inode getInode(Long inodeId) throws DelaException { - if (inodeId == null) { - throw new DelaException(RESTCodes.DelaErrorCode.ILLEGAL_ARGUMENT, Level.FINE, DelaException.Source.LOCAL, - "inodeId was not provided."); - } - Inode inode = inodeFacade.findById(inodeId); - if (inode == null) { - throw new DelaException(RESTCodes.DelaErrorCode.INODE_NOT_FOUND, Level.FINE, DelaException.Source.LOCAL); - } - return inode; - } - -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaService.java deleted file mode 100644 index 4a2a101a91..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/DelaService.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.dela; - -import com.google.common.base.Strings; -import com.google.gson.Gson; -import io.hops.hopsworks.api.dela.dto.BootstrapDTO; -import io.hops.hopsworks.api.dela.dto.DelaClientDTO; -import io.hops.hopsworks.api.filter.Audience; -import io.hops.hopsworks.api.filter.NoCacheResponse; -import io.hops.hopsworks.api.hopssite.dto.LocalDatasetDTO; -import io.hops.hopsworks.api.hopssite.dto.LocalDatasetHelper; -import io.hops.hopsworks.api.jwt.JWTHelper; -import io.hops.hopsworks.common.dataset.DatasetController; -import io.hops.hopsworks.common.dataset.FilePreviewDTO; -import io.hops.hopsworks.common.hdfs.DistributedFileSystemOps; -import io.hops.hopsworks.common.hdfs.DistributedFsService; -import io.hops.hopsworks.common.project.ProjectController; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.DelaDatasetController; -import io.hops.hopsworks.dela.RemoteDelaController; -import io.hops.hopsworks.dela.TransferDelaController; -import io.hops.hopsworks.dela.dto.common.ClusterAddressDTO; -import io.hops.hopsworks.dela.dto.hopssite.SearchServiceDTO; -import io.hops.hopsworks.dela.dto.hopsworks.HopsworksSearchDTO; -import io.hops.hopsworks.dela.hopssite.HopssiteController; -import io.hops.hopsworks.dela.old_dto.ElementSummaryJSON; -import io.hops.hopsworks.dela.old_dto.ErrorDescJSON; -import io.hops.hopsworks.dela.old_dto.HopsContentsSummaryJSON; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.jwt.annotation.JWTRequired; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import io.hops.hopsworks.persistence.entity.project.team.ProjectTeam; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.restutils.RESTCodes; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Path("/dela") -@Stateless -@JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@Api(value = "Dela Service", - description = "Dela Service") -@TransactionAttribute(TransactionAttributeType.NEVER) -public class DelaService { - - private static final Logger LOGGER = Logger.getLogger(DelaService.class.getName()); - @EJB - private NoCacheResponse noCacheResponse; - @EJB - private Settings settings; - @EJB - private HopssiteController hopsSite; - @EJB - private ProjectController projectCtrl; - @EJB - private TransferDelaController delaTransferCtrl; - @EJB - private RemoteDelaController remoteDelaCtrl; - @EJB - private DelaDatasetController delaDatasetCtrl; - - @EJB - private DatasetController datasetCtrl; - @EJB - private DistributedFsService dfs; - @EJB - private JWTHelper jWTHelper; - - @GET - @Path("/client") - @Produces(MediaType.APPLICATION_JSON) - public Response getClientType(@Context SecurityContext sc) { - DelaClientDTO clientType = new DelaClientDTO(settings.getDelaClientType().type); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(clientType).build(); - } - - @GET - @Produces(MediaType.APPLICATION_JSON) - public Response getPublicDatasets(@Context SecurityContext sc) { - List clusterDatasets = delaDatasetCtrl.getLocalPublicDatasets(); - DistributedFileSystemOps dfso = dfs.getDfsOps(); - List localDS; - try { - localDS = LocalDatasetHelper.parse(datasetCtrl, dfso, clusterDatasets); - } finally { - dfs.closeDfsClient(dfso); - } - GenericEntity> datasets = new GenericEntity>(localDS) {}; - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(datasets).build(); - } - //******************************************************************************************************************** - @GET - @Path("/search") - @Produces(MediaType.APPLICATION_JSON) - public Response publicSearch(@ApiParam(required=true) @QueryParam("query") String query, @Context SecurityContext sc) - throws DelaException { - LOGGER.log(Settings.DELA_DEBUG, "dela:search"); - if (Strings.isNullOrEmpty(query)) { - throw new DelaException(RESTCodes.DelaErrorCode.ILLEGAL_ARGUMENT, Level.FINE, DelaException.Source.LOCAL, - "query param was not provided."); - } - SearchServiceDTO.SearchResult searchResult = hopsSite.search(query); - SearchServiceDTO.Item[] pageResult = hopsSite.page(searchResult.getSessionId(), 0, searchResult.getNrHits()); - HopsworksSearchDTO.Item[] parsedResult = parseSearchResult(pageResult); - String auxResult = new Gson().toJson(parsedResult); - LOGGER.log(Settings.DELA_DEBUG, "dela:search:done"); - return success(auxResult); - } - - private HopsworksSearchDTO.Item[] parseSearchResult(SearchServiceDTO.Item[] items) { - HopsworksSearchDTO.Item[] result = new HopsworksSearchDTO.Item[items.length]; - for (int i = 0; i < items.length; i++) { - result[i] = new HopsworksSearchDTO.Item(items[i]); - } - return result; - } - //******************************************************************************************************************** - @GET - @Path("/transfers/{publicDSId}") - @Produces(MediaType.APPLICATION_JSON) - public Response details(@PathParam("publicDSId")String publicDSId, @Context SecurityContext sc) throws DelaException { - LOGGER.log(Settings.DELA_DEBUG, "dela:dataset:details {0}", publicDSId); - SearchServiceDTO.ItemDetails result = hopsSite.details(publicDSId); - String auxResult = new Gson().toJson(result); - LOGGER.log(Settings.DELA_DEBUG, "dela:dataset:details:done {0}", publicDSId); - return success(auxResult); - } - - public enum TransfersFilter { - USER - } - - @GET - @Path("/transfers") - @Produces(MediaType.APPLICATION_JSON) - public Response getContentsForUser(@Context SecurityContext sc, @QueryParam("filter") TransfersFilter filter) - throws DelaException { - if (!filter.equals(TransfersFilter.USER)) { - throw new DelaException(RESTCodes.DelaErrorCode.ILLEGAL_ARGUMENT, Level.FINE, DelaException.Source.LOCAL, - "not handling filter value:" + filter); - } - Users user = jWTHelper.getUserPrincipal(sc); - List teams = projectCtrl.findProjectByUser(user.getEmail()); - List projectIds = new LinkedList<>(); - for (ProjectTeam t : teams) { - projectIds.add(t.getProject().getId()); - } - - HopsContentsSummaryJSON.Contents resp = delaTransferCtrl.getContents(projectIds); - List userContents = new ArrayList<>(); - Iterator> it = resp.getContents().entrySet().iterator(); - while (it.hasNext()) { - Map.Entry n = it.next(); - userContents.add(new UserContentsSummaryJSON(n.getKey(), n.getValue())); - } - GenericEntity> userContentsList - = new GenericEntity>(userContents) { - }; - return success(userContentsList); - } - //******************************************************************************************************************** - @POST - @Path("/datasets/{publicDSId}/readme") - @Produces(MediaType.APPLICATION_JSON) - @ApiOperation(value = "Gets readme file from a provided list of peers") - public Response readme(@PathParam("publicDSId") String publicDSId, BootstrapDTO peersJSON, - @Context SecurityContext sc) throws DelaException { - Optional readme = tryReadmeLocally(publicDSId); - if (!readme.isPresent()) { - readme = tryReadmeRemotely(publicDSId, peersJSON); - } - if (!readme.isPresent()) { - throw new DelaException(RESTCodes.DelaErrorCode.README_RETRIEVAL_FAILED, Level.FINE, - DelaException.Source.REMOTE_DELA, "no local or remote version of readme found"); - } - return success(readme.get()); - } - - private Optional tryReadmeLocally(String publicDSId) throws DelaException { - Optional dataset = delaDatasetCtrl.isPublicDatasetLocal(publicDSId); - if(dataset.isPresent()) { - try { - FilePreviewDTO readme = delaDatasetCtrl.getLocalReadmeForPublicDataset(dataset.get()); - return Optional.of(readme); - } catch (IOException | IllegalAccessException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ILLEGAL_ARGUMENT, Level.SEVERE, - DelaException.Source.HDFS, null, ex.getMessage(), ex); - } - } - return Optional.empty(); - } - - private Optional tryReadmeRemotely(String publicDSId, BootstrapDTO peersJSON) { - for(ClusterAddressDTO peer : peersJSON.getBootstrap()) { - try { - FilePreviewDTO readme = remoteDelaCtrl.readme(publicDSId, peer); - return Optional.of(readme); - } catch (DelaException ex) { - continue; - } - } - return Optional.empty(); - } - //******************************************************************************************************************** - private Response success(Object content) { - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(content).build(); - } - - - private Response errorResponse(String msg) { - ErrorDescJSON errorDesc = new ErrorDescJSON(msg); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.EXPECTATION_FAILED).entity(errorDesc).build(); - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/RemoteDelaService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/RemoteDelaService.java deleted file mode 100644 index 8be5a39b3f..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/RemoteDelaService.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.dela; - -import io.hops.hopsworks.api.filter.Audience; -import io.hops.hopsworks.api.filter.NoCacheResponse; -import io.hops.hopsworks.common.dao.dataset.DatasetFacade; -import io.hops.hopsworks.common.dataset.FilePreviewDTO; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.DelaHdfsController; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.jwt.annotation.JWTRequired; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import io.hops.hopsworks.restutils.RESTCodes; -import io.swagger.annotations.Api; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Path("/remote/dela") -@Stateless -@JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@Api(value = "Cross Dela Service", - description = "Cross Dela Service") -@TransactionAttribute(TransactionAttributeType.NEVER) -public class RemoteDelaService { - - private static final Logger LOGGER = Logger.getLogger(RemoteDelaService.class.getName()); - @EJB - private NoCacheResponse noCacheResponse; - @EJB - private DelaHdfsController hdfsDelaCtrl; - @EJB - private DatasetFacade datasetFacade; - - @GET - @Path("/datasets/{publicDSId}/readme") - @Produces(MediaType.APPLICATION_JSON) - public Response readme(@PathParam("publicDSId") String publicDSId, @Context SecurityContext sc) throws DelaException { - LOGGER.log(Settings.DELA_DEBUG, "remote:dela:readme {0}", publicDSId); - Optional dataset = datasetFacade.findByPublicDsId(publicDSId); - if (!dataset.isPresent() || !dataset.get().isPublicDs()) { - throw new DelaException(RESTCodes.DelaErrorCode.DATASET_DOES_NOT_EXIST, Level.FINE, - DelaException.Source.REMOTE_DELA); - } - FilePreviewDTO result = hdfsDelaCtrl.getPublicReadme(dataset.get()); - LOGGER.log(Settings.DELA_DEBUG, "remote:dela:readme - done {0}", publicDSId); - return success(result); - } - - private Response success(Object content) { - return noCacheResponse.getNoCacheCORSResponseBuilder(Response.Status.OK).entity(content).build(); - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/UserContentsSummaryJSON.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/UserContentsSummaryJSON.java deleted file mode 100644 index 3720bbcb1f..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/UserContentsSummaryJSON.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.dela; - -import io.hops.hopsworks.dela.old_dto.ElementSummaryJSON; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class UserContentsSummaryJSON { - - private Integer projectId; - private ElementSummaryJSON[] elementSummaries = new ElementSummaryJSON[0]; - - public UserContentsSummaryJSON() { - } - - public UserContentsSummaryJSON(Integer projectId, - ElementSummaryJSON[] elementSummaries) { - this.projectId = projectId; - if (elementSummaries != null) { - this.elementSummaries = elementSummaries; - } - } - - public Integer getProjectId() { - return projectId; - } - - public void setProjectId(Integer projectId) { - this.projectId = projectId; - } - - public ElementSummaryJSON[] getElementSummaries() { - return elementSummaries; - } - - public void setElementSummaries(ElementSummaryJSON[] elementSummaries) { - if (elementSummaries != null) { - this.elementSummaries = elementSummaries; - } else { - this.elementSummaries = new ElementSummaryJSON[0]; - } - } - - @Override - public String toString() { - return "UserContentsSummaryJSON{" + "projectId=" + projectId + ", elementSummaries=" + elementSummaries + '}'; - } - -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/BootstrapDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/BootstrapDTO.java deleted file mode 100644 index 73eed20b0b..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/BootstrapDTO.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.dela.dto; - -import io.hops.hopsworks.dela.dto.common.ClusterAddressDTO; -import java.util.List; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class BootstrapDTO { - private List bootstrap; - - public List getBootstrap() { - return bootstrap; - } - - public void setBootstrap(List bootstrap) { - this.bootstrap = bootstrap; - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/DelaClientDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/DelaClientDTO.java deleted file mode 100644 index 617351a27e..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/DelaClientDTO.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.dela.dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class DelaClientDTO { - - private String clientType; - - public DelaClientDTO() { - } - - public DelaClientDTO(String clientType) { - this.clientType = clientType; - } - - public String getClientType() { - return clientType; - } - - public void setClientType(String clientType) { - this.clientType = clientType; - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/InodeIdDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/InodeIdDTO.java deleted file mode 100644 index ac10cfe449..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/dela/dto/InodeIdDTO.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.dela.dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class InodeIdDTO { - private Long id; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/CommentService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/CommentService.java deleted file mode 100644 index 65f22ab70d..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/CommentService.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite; - -import io.hops.hopsworks.api.filter.Audience; -import io.hops.hopsworks.api.filter.NoCacheResponse; -import io.hops.hopsworks.api.hopssite.dto.CommentIssueReqDTO; -import io.hops.hopsworks.api.jwt.JWTHelper; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.dto.hopssite.CommentDTO; -import io.hops.hopsworks.dela.dto.hopssite.CommentIssueDTO; -import io.hops.hopsworks.dela.hopssite.HopsSite; -import io.hops.hopsworks.dela.hopssite.HopssiteController; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.jwt.annotation.JWTRequired; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.util.SettingsHelper; - -import javax.ejb.EJB; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.enterprise.context.RequestScoped; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.util.List; -import java.util.logging.Logger; - -@RequestScoped -@TransactionAttribute(TransactionAttributeType.NEVER) -public class CommentService { - - private final static Logger LOG = Logger.getLogger(CommentService.class.getName()); - @EJB - private HopssiteController hopsSite; - @EJB - private Settings settings; - @EJB - private NoCacheResponse noCacheResponse; - @EJB - private JWTHelper jWTHelper; - private String publicDSId; - - public CommentService() { - } - - public void setPublicDSId(String publicDSId) { - this.publicDSId = publicDSId; - } - - @GET - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response getAllComments(@Context SecurityContext sc) throws DelaException { - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:get:all {0}", publicDSId); - List comments = hopsSite.getDatasetAllComments(publicDSId); - GenericEntity> commentsJson - = new GenericEntity>(comments) { - }; - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:get:all - done{0}", publicDSId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(commentsJson).build(); - } - - @POST - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response addComment(String content, @Context SecurityContext sc) throws DelaException { - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:add {0}", publicDSId); - Users user = jWTHelper.getUserPrincipal(sc); - String publicCId = SettingsHelper.clusterId(settings); - CommentDTO.Publish comment = new CommentDTO.Publish(user.getEmail(), content); - hopsSite.performAsUser(user, new HopsSite.UserFunc() { - @Override - public String perform() throws DelaException { - hopsSite.addComment(publicCId, publicDSId, comment); - return "ok"; - } - }); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:add - done {0}", publicDSId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).build(); - } - - @PUT - @Path("{commentId}") - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response updateComment(@Context SecurityContext sc, @PathParam("commentId") Integer commentId, - String content) throws DelaException { - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:update {0}", publicDSId); - Users user = jWTHelper.getUserPrincipal(sc); - String publicCId = SettingsHelper.clusterId(settings); - CommentDTO.Publish comment = new CommentDTO.Publish(user.getEmail(), content); - hopsSite.performAsUser(user, new HopsSite.UserFunc() { - @Override - public String perform() throws DelaException { - hopsSite.updateComment(publicCId, publicDSId, commentId, comment); - return "ok"; - } - }); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:done {0}", publicDSId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).build(); - } - - @DELETE - @Path("{commentId}") - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response deleteComment(@Context SecurityContext sc, @PathParam("commentId") Integer commentId) - throws DelaException, DelaException, DelaException { - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:delete {0}", publicDSId); - Users user = jWTHelper.getUserPrincipal(sc); - String publicCId = SettingsHelper.clusterId(settings); - hopsSite.performAsUser(user, new HopsSite.UserFunc() { - @Override - public String perform() throws DelaException { - hopsSite.removeComment(publicCId, publicDSId, commentId, user.getEmail()); - return "ok"; - } - }); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:delete - done {0}", publicDSId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).build(); - } - - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Path("{commentId}/report") - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response reportAbuse(@Context SecurityContext sc, @PathParam("commentId") Integer commentId, - CommentIssueReqDTO commentReqIssue) throws DelaException { - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:report {0}", publicDSId); - String publicCId = SettingsHelper.clusterId(settings); - Users user = jWTHelper.getUserPrincipal(sc); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:report issue:{0}", commentReqIssue); -// CommentIssueDTO commentIssue -// = new CommentIssueDTO(commentReqIssue.getType(), commentReqIssue.getMsg(), user.getEmail()); - CommentIssueDTO commentIssue = new CommentIssueDTO("default-type", "default-issue", user.getEmail()); - hopsSite.performAsUser(user, new HopsSite.UserFunc() { - @Override - public String perform() throws DelaException { - hopsSite.reportComment(publicCId, publicDSId, commentId, commentIssue); - return "ok"; - } - }); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:report - done {0}", publicDSId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).build(); - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/HopssiteService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/HopssiteService.java deleted file mode 100644 index 09e7b0a387..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/HopssiteService.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite; - -import io.hops.hopsworks.api.filter.Audience; -import io.hops.hopsworks.api.filter.NoCacheResponse; -import io.hops.hopsworks.api.hopssite.dto.CategoryDTO; -import io.hops.hopsworks.api.hopssite.dto.DatasetIssueReqDTO; -import io.hops.hopsworks.api.hopssite.dto.HopsSiteServiceInfoDTO; -import io.hops.hopsworks.api.hopssite.dto.LocalDatasetDTO; -import io.hops.hopsworks.api.jwt.JWTHelper; -import io.hops.hopsworks.common.dao.dataset.DatasetFacade; -import io.hops.hopsworks.common.dao.hdfs.inode.InodeFacade; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.dto.common.UserDTO; -import io.hops.hopsworks.dela.dto.hopssite.DatasetDTO; -import io.hops.hopsworks.dela.dto.hopssite.HopsSiteDatasetDTO; -import io.hops.hopsworks.dela.hopssite.HopssiteController; -import io.hops.hopsworks.dela.old_hopssite_dto.DatasetIssueDTO; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.jwt.annotation.JWTRequired; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import io.hops.hopsworks.persistence.entity.hdfs.inode.Inode; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiParam; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.inject.Inject; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Path("/hopssite/") -@Stateless -@JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@Api(value = "Hopssite Service", - description = "Hopssite Service") -@TransactionAttribute(TransactionAttributeType.NEVER) -public class HopssiteService { - - private final static Logger LOGGER = Logger.getLogger(HopssiteService.class.getName()); - @EJB - private NoCacheResponse noCacheResponse; - @EJB - private HopssiteController hopsSite; - @EJB - private Settings settings; - @EJB - private DatasetFacade datasetFacade; - @EJB - private InodeFacade inodes; - @Inject - private CommentService commentService; - @Inject - private RatingService ratingService; - @EJB - private JWTHelper jWTHelper; - - @GET - @Path("services/{service}") - public Response getServiceInfo(@PathParam("service") String service, @Context SecurityContext sc) { - boolean delaEnabled = settings.isDelaEnabled(); - HopsSiteServiceInfoDTO serviceInfo; - if (delaEnabled) { - serviceInfo = new HopsSiteServiceInfoDTO("Dela", 1, "Dela enabled."); - } else { - serviceInfo = new HopsSiteServiceInfoDTO("Dela", 0, "Dela disabled."); - } - - LOGGER.log(Settings.DELA_DEBUG, "Get service info for service: {0}, {1}", new Object[]{service, serviceInfo}); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(serviceInfo).build(); - } - - @GET - @Path("clusterId") - @Produces(MediaType.APPLICATION_JSON) - public Response getClusterId(@Context SecurityContext sc) throws DelaException { - String clusterId = settings.getDELA_CLUSTER_ID(); - LOGGER.log(Level.INFO, "Cluster id on hops-site: {0}", clusterId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(clusterId).build(); - } - - @GET - @Path("userId") - public Response getUserId(@Context SecurityContext sc) throws DelaException { - Users user = jWTHelper.getUserPrincipal(sc); - String id = String.valueOf(hopsSite.getUserId(user.getEmail())); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(id).build(); - } - - public static enum CategoriesFilter { - ALL, - TOP_RATED, - NEW - } - @GET - @Path("datasets") - public Response getAllDatasets(@ApiParam(required = true) @QueryParam("filter") CategoriesFilter filter, - @Context SecurityContext sc) throws DelaException { - List datasets; - switch(filter) { - case ALL : datasets = hopsSite.getAll(); break; - case TOP_RATED : datasets = hopsSite.getAll(); break; - case NEW : datasets = hopsSite.getAll(); break; - default: throw new IllegalArgumentException("unknown filter:" + filter); - } - - markLocalDatasets(datasets); - GenericEntity> datasetsJson = new GenericEntity>(datasets) { - }; - LOGGER.log(Settings.DELA_DEBUG, "Get all datasets"); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(datasetsJson).build(); - } - - @GET - @Path("datasets/{publicDSId}") - public Response getDataset(@PathParam("publicDSId") String publicDSId, @Context SecurityContext sc) - throws DelaException { - DatasetDTO.Complete datasets = hopsSite.getDataset(publicDSId); - LOGGER.log(Settings.DELA_DEBUG, "Get a dataset"); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(datasets).build(); - } - - @GET - @Path("datasets/{publicDSId}/local") - public Response getLocalDataset(@PathParam("publicDSId") String publicDSId, @Context SecurityContext sc) { - Optional datasets = datasetFacade.findByPublicDsId(publicDSId); - if (!datasets.isPresent()) { - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.BAD_REQUEST).build(); - } - Dataset ds = datasets.get(); - Inode parent = inodes.findParent(ds.getInode()); // to get the real parent project - LocalDatasetDTO datasetDTO = new LocalDatasetDTO(ds.getInodeId(), ds.getName(), ds.getDescription(), parent. - getInodePK().getName()); - LOGGER.log(Settings.DELA_DEBUG, "Get a local dataset by public id."); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(datasetDTO).build(); - } - - @GET - @Path("categories") - public Response getDisplayCategories(@Context SecurityContext sc) { - CategoryDTO categoryAll = new CategoryDTO(CategoriesFilter.ALL.name(), "All", false); - CategoryDTO categoryNew = new CategoryDTO(CategoriesFilter.NEW.name(), "Recently added", false); - CategoryDTO categoryTopRated = new CategoryDTO(CategoriesFilter.TOP_RATED.name(), "Top Rated", false); - List categories = Arrays.asList(categoryAll, categoryNew, categoryTopRated); - GenericEntity> categoriesEntity = new GenericEntity>(categories) { - }; - LOGGER.log(Settings.DELA_DEBUG, "Get all display categories."); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(categoriesEntity).build(); - } - - @POST - @Path("datasets/{publicDSId}/issue") - public Response addDatasetIssue(@PathParam("publicDSId") String publicDSId, DatasetIssueReqDTO datasetIssueReq, - @Context SecurityContext sc) throws DelaException { - if (datasetIssueReq == null) { - throw new IllegalArgumentException("Dataset issue not set."); - } - Users u = jWTHelper.getUserPrincipal(sc); - UserDTO.Complete user = hopsSite.getUser(u.getEmail()); - DatasetIssueDTO datasetIssue = new DatasetIssueDTO(publicDSId, user, datasetIssueReq.getType(), - datasetIssueReq.getMsg()); - boolean added = hopsSite.addDatasetIssue(datasetIssue); - if (added) { - LOGGER.log(Settings.DELA_DEBUG, "Added issue for dataset {0}", publicDSId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).build(); - } - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.NOT_MODIFIED).build(); - } - - @Path("datasets/{publicDSId}/comments") - public CommentService getComments(@PathParam("publicDSId") String publicDSId) { - this.commentService.setPublicDSId(publicDSId); - return this.commentService; - } - - @Path("datasets/{publicDSId}/rating") - public RatingService getRating(@PathParam("publicDSId") String publicDSId) { - this.ratingService.setPublicDSId(publicDSId); - return this.ratingService; - } - - private void markLocalDatasets(List datasets) { - List publicDatasets = datasetFacade.findAllPublicDatasets(); - for (HopsSiteDatasetDTO publicDs : datasets) { - for (Dataset localDs : publicDatasets) { - if (publicDs.getPublicId().equals(localDs.getPublicDsId())) { - publicDs.setLocalDataset(true); - break; - } - } - } - } - -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/RatingService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/RatingService.java deleted file mode 100644 index c3b3b14640..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/RatingService.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite; - -import io.hops.hopsworks.api.filter.Audience; -import io.hops.hopsworks.api.filter.NoCacheResponse; -import io.hops.hopsworks.api.hopssite.dto.RatingValueDTO; -import io.hops.hopsworks.api.jwt.JWTHelper; -import io.hops.hopsworks.jwt.annotation.JWTRequired; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.dto.hopssite.RateDTO; -import io.hops.hopsworks.dela.dto.hopssite.RatingDTO; -import io.hops.hopsworks.dela.hopssite.HopsSite; -import io.hops.hopsworks.dela.hopssite.HopssiteController; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.restutils.RESTCodes; -import io.hops.hopsworks.util.SettingsHelper; -import io.swagger.annotations.ApiParam; - -import javax.ejb.EJB; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.enterprise.context.RequestScoped; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.util.logging.Level; -import java.util.logging.Logger; - -@RequestScoped -@TransactionAttribute(TransactionAttributeType.NEVER) -public class RatingService { - - private static final Logger LOGGER = Logger.getLogger(RatingService.class.getName()); - @EJB - private HopssiteController hopsSite; - @EJB - private Settings settings; - @EJB - private NoCacheResponse noCacheResponse; - @EJB - private JWTHelper jWTHelper; - - private String publicDSId; - - public void setPublicDSId(String publicDSId) { - this.publicDSId = publicDSId; - } - - public static enum RatingFilter { - - USER, - DATASET - } - - @GET - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response getRating(@ApiParam(required = true) @QueryParam("filter") RatingFilter filter, - @Context SecurityContext sc) throws DelaException { - switch (filter) { - case DATASET: - return getDatasetAllRating(); - case USER: - return getDatasetUserRating(sc); - default: - throw new DelaException(RESTCodes.DelaErrorCode.ILLEGAL_ARGUMENT, Level.FINE, DelaException.Source.HOPS_SITE, - "unknown filter value:" + filter + " - accepted dataset/user"); - } - } - - private Response getDatasetAllRating() throws DelaException { - LOGGER.log(Settings.DELA_DEBUG, "hops-site:rating:get:all {0}", publicDSId); - RatingDTO rating = hopsSite.getDatasetAllRating(publicDSId); - LOGGER.log(Settings.DELA_DEBUG, "hops-site:rating:get:all - done {0}", publicDSId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(rating).build(); - } - - private Response getDatasetUserRating(SecurityContext sc) throws DelaException { - LOGGER.log(Settings.DELA_DEBUG, "hops-site:rating:get:user {0}", publicDSId); - String publicCId = SettingsHelper.clusterId(settings); - Users user = jWTHelper.getUserPrincipal(sc); - RatingDTO rating = hopsSite.getDatasetUserRating(publicCId, publicDSId, user.getEmail()); - LOGGER.log(Settings.DELA_DEBUG, "hops-site:rating:get:user - done {0}", publicDSId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(rating).build(); - } - - @POST - @JWTRequired(acceptedTokens={Audience.API}, allowedUserRoles={"HOPS_ADMIN", "HOPS_USER"}) - public Response addRating(@Context SecurityContext sc, RatingValueDTO rating) throws DelaException { - LOGGER.log(Settings.DELA_DEBUG, "hops-site:rating:add {0}", publicDSId); - String publicCId = SettingsHelper.clusterId(settings); - Users user = jWTHelper.getUserPrincipal(sc); - hopsSite.performAsUser(user, new HopsSite.UserFunc() { - @Override - public String perform() throws DelaException { - RateDTO datasetRate = new RateDTO(user.getEmail(), rating.getValue()); - hopsSite.addRating(publicCId, publicDSId, datasetRate); - return "ok"; - } - }); - LOGGER.log(Settings.DELA_DEBUG, "hops-site:rating:add - done {0}", publicDSId); - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).build(); - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CategoryDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CategoryDTO.java deleted file mode 100644 index f66d15c74b..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CategoryDTO.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class CategoryDTO { - - private String categoryName; - private String displayName; - private boolean parentCategory; - - public CategoryDTO() { - } - - public CategoryDTO(String categoryName, String displayName, boolean parentCategory) { - this.categoryName = categoryName; - this.displayName = displayName; - this.parentCategory = parentCategory; - } - - public String getCategoryName() { - return categoryName; - } - - public void setCategoryName(String categoryName) { - this.categoryName = categoryName; - } - - public String getDisplayName() { - return displayName; - } - - public void setDisplayName(String displayName) { - this.displayName = displayName; - } - - public boolean isParentCategory() { - return parentCategory; - } - - public void setParentCategory(boolean parentCategory) { - this.parentCategory = parentCategory; - } - - - @Override - public String toString() { - return "CategoryDTO{" + "categoryName=" + categoryName + ", displayName=" + displayName + '}'; - } - -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CommentIssueReqDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CommentIssueReqDTO.java deleted file mode 100644 index 185523cdbb..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CommentIssueReqDTO.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class CommentIssueReqDTO { - - private String type; - private String msg; - - public CommentIssueReqDTO() { - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - -} \ No newline at end of file diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CommentReqDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CommentReqDTO.java deleted file mode 100644 index 55622d0895..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/CommentReqDTO.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class CommentReqDTO { - - private String content; - private DatasetReqDTO dataset; - - public CommentReqDTO() { - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public DatasetReqDTO getDataset() { - return dataset; - } - - public void setDataset(DatasetReqDTO dataset) { - this.dataset = dataset; - } - -} \ No newline at end of file diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/DatasetIssueReqDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/DatasetIssueReqDTO.java deleted file mode 100644 index 5d758e714d..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/DatasetIssueReqDTO.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class DatasetIssueReqDTO { - - private String type; - private String msg; - - public DatasetIssueReqDTO() { - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } -} \ No newline at end of file diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/DatasetReqDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/DatasetReqDTO.java deleted file mode 100644 index d3e4363ca2..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/DatasetReqDTO.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class DatasetReqDTO { - - private Integer id; - private String publicId; - private String clusterId; - - public DatasetReqDTO() { - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getPublicId() { - return publicId; - } - - public void setPublicId(String publicId) { - this.publicId = publicId; - } - - public String getClusterId() { - return clusterId; - } - - public void setClusterId(String clusterId) { - this.clusterId = clusterId; - } -} \ No newline at end of file diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/HopsSiteServiceInfoDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/HopsSiteServiceInfoDTO.java deleted file mode 100644 index f9ba3bdd04..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/HopsSiteServiceInfoDTO.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class HopsSiteServiceInfoDTO { - - private String name; - private int status; - private String msg; - - public HopsSiteServiceInfoDTO() { - } - - public HopsSiteServiceInfoDTO(String name, int status, String msg) { - this.name = name; - this.status = status; - this.msg = msg; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getStatus() { - return status; - } - - public void setStatus(int status) { - this.status = status; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - - @Override - public String toString() { - return "Service{" + "name=" + name + ", status=" + status + ", msg=" + msg + '}'; - } - -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/LocalDatasetDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/LocalDatasetDTO.java deleted file mode 100644 index eda608b784..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/LocalDatasetDTO.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import java.util.Date; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class LocalDatasetDTO { - private Long inodeId; - private String name; - private String description; - private String projectName; - private Date createDate; - private Date publishedOn; - private long size; - - public LocalDatasetDTO() { - } - - public LocalDatasetDTO(Long InodeId, String projectName) { - this.inodeId = InodeId; - this.projectName = projectName; - } - - public LocalDatasetDTO(Long inodeId, String name, String description, String projectName) { - this.inodeId = inodeId; - this.name = name; - this.description = description; - this.projectName = projectName; - } - - public LocalDatasetDTO(Long inodeId, String name, String description, String projectName, Date createDate, - Date publishedOn, long size) { - this.inodeId = inodeId; - this.name = name; - this.description = description; - this.projectName = projectName; - this.createDate = createDate; - this.publishedOn = publishedOn; - this.size = size; - } - - public Long getInodeId() { - return inodeId; - } - - public void setInodeId(Long InodeId) { - this.inodeId = InodeId; - } - - public String getProjectName() { - return projectName; - } - - public void setProjectName(String projectName) { - this.projectName = projectName; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public Date getCreateDate() { - return createDate; - } - - public void setCreateDate(Date createDate) { - this.createDate = createDate; - } - - public Date getPublishedOn() { - return publishedOn; - } - - public void setPublishedOn(Date publishedOn) { - this.publishedOn = publishedOn; - } - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/LocalDatasetHelper.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/LocalDatasetHelper.java deleted file mode 100644 index 6af6c90c74..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/LocalDatasetHelper.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import io.hops.hopsworks.common.dataset.DatasetController; -import io.hops.hopsworks.common.hdfs.DistributedFileSystemOps; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import org.apache.hadoop.fs.Path; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class LocalDatasetHelper { - - public static List parse(DatasetController datasetCtrl, DistributedFileSystemOps dfso, - List datasets) { - List localDS = new ArrayList<>(); - for (Dataset d : datasets) { - Date date = new Date(d.getInode().getModificationTime().longValue()); - Path path = datasetCtrl.getDatasetPath(d); - long size; - //TODO Alex - if we want to still get size, it should either be immutable for dataset, - //or updated periodically into the db and provide this approximate version when requested - size = -1; -// size = dfso.getLastUpdatedDatasetSize(path); - localDS.add(new LocalDatasetDTO(d.getInodeId(), d.getName(), d.getDescription(), d.getProject().getName(), date, - date, size)); - } - return localDS; - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/RateReqDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/RateReqDTO.java deleted file mode 100644 index e04bd268c3..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/RateReqDTO.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import java.util.Date; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class RateReqDTO { - - private int rating; - private Date datePublished; - private DatasetReqDTO dataset; - - public RateReqDTO() { - } - - public int getRating() { - return rating; - } - - public void setRating(int rating) { - this.rating = rating; - } - - public Date getDatePublished() { - return datePublished; - } - - public void setDatePublished(Date datePublished) { - this.datePublished = datePublished; - } - - public DatasetReqDTO getDataset() { - return dataset; - } - - public void setDataset(DatasetReqDTO dataset) { - this.dataset = dataset; - } - -} \ No newline at end of file diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/RatingValueDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/RatingValueDTO.java deleted file mode 100644 index 606c15c3b4..0000000000 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/hopssite/dto/RatingValueDTO.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.api.hopssite.dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class RatingValueDTO { - private int value; - - public int getValue() { - return value; - } - - public void setValue(int value) { - this.value = value; - } -} diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/ProjectService.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/ProjectService.java index ed3ab1c47d..5ba43d0430 100644 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/ProjectService.java +++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/project/ProjectService.java @@ -42,7 +42,6 @@ import io.hops.hopsworks.api.airflow.AirflowService; import io.hops.hopsworks.api.alert.AlertResource; import io.hops.hopsworks.api.dataset.DatasetResource; -import io.hops.hopsworks.api.dela.DelaProjectService; import io.hops.hopsworks.api.opensearch.OpenSearchResource; import io.hops.hopsworks.api.experiments.ExperimentsResource; import io.hops.hopsworks.api.featurestore.FeaturestoreService; @@ -204,8 +203,6 @@ public class ProjectService { @EJB private JWTHelper jWTHelper; @Inject - private DelaProjectService delaService; - @Inject private InferenceResource inference; @Inject private ProjectActivitiesResource activitiesResource; @@ -804,13 +801,7 @@ public PythonResource python(@PathParam("projectId") Integer id) { this.pythonResource.setProjectId(id); return this.pythonResource; } - - @Path("{projectId}/dela") - public DelaProjectService dela(@PathParam("projectId") Integer id) { - this.delaService.setProjectId(id); - return this.delaService; - } - + @Path("{projectId}/activities") public ProjectActivitiesResource activities(@PathParam("projectId") Integer id) { this.activitiesResource.setProjectId(id); diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/VersionsDTO.java b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/VersionsDTO.java index fe156b7102..03a31f6d8c 100644 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/VersionsDTO.java +++ b/hopsworks-api/src/main/java/io/hops/hopsworks/api/util/VersionsDTO.java @@ -89,7 +89,6 @@ public VersionsDTO(Settings settings) { versions.add(new Version("spark", settings.getSparkVersion())); versions.add(new Version("flink", settings.getFlinkVersion())); versions.add(new Version("epipe", settings.getEpipeVersion())); - versions.add(new Version("dela", settings.getDelaVersion())); versions.add(new Version("kafka", settings.getKafkaVersion())); versions.add(new Version("opensearch", settings.getOpenSearchVersion())); versions.add(new Version("tensorflow", settings.getTensorflowVersion())); diff --git a/hopsworks-api/src/main/java/io/hops/hopsworks/rest/application/config/ApplicationConfig.java b/hopsworks-api/src/main/java/io/hops/hopsworks/rest/application/config/ApplicationConfig.java index d676f2bba3..c0ed79dfac 100644 --- a/hopsworks-api/src/main/java/io/hops/hopsworks/rest/application/config/ApplicationConfig.java +++ b/hopsworks-api/src/main/java/io/hops/hopsworks/rest/application/config/ApplicationConfig.java @@ -115,15 +115,6 @@ public ApplicationConfig() { register(org.glassfish.jersey.media.multipart.MultiPartFeature.class); - //dela - register(io.hops.hopsworks.api.dela.DelaClusterService.class); - register(io.hops.hopsworks.api.dela.DelaService.class); - register(io.hops.hopsworks.api.dela.DelaProjectService.class); - register(io.hops.hopsworks.api.dela.RemoteDelaService.class); - register(io.hops.hopsworks.api.hopssite.HopssiteService.class); - register(io.hops.hopsworks.api.hopssite.CommentService.class); - register(io.hops.hopsworks.api.hopssite.RatingService.class); - //maggy register(io.hops.hopsworks.api.maggy.MaggyService.class); diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/ApplicationConfig.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/ApplicationConfig.java index 1d86e25c9e..b7909d2db1 100644 --- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/ApplicationConfig.java +++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/ApplicationConfig.java @@ -56,7 +56,6 @@ public ApplicationConfig() { register(io.hops.hopsworks.ca.api.certificates.ProjectCertsResource.class); register(io.hops.hopsworks.ca.api.certificates.HostCertsResource.class); register(io.hops.hopsworks.ca.api.certificates.AppCertsResource.class); - register(io.hops.hopsworks.ca.api.certificates.DelaTrackerCertsResource.class); register(io.hops.hopsworks.ca.api.token.TokenResources.class); register(io.hops.hopsworks.ca.api.certificates.CRLResource.class); diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/CertificatesResource.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/CertificatesResource.java index c5977a8348..5485a692b2 100644 --- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/CertificatesResource.java +++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/CertificatesResource.java @@ -75,8 +75,6 @@ public class CertificatesResource { @Inject private AppCertsResource appCertsResource; @Inject - private DelaTrackerCertsResource delaTrackerCertsResource; - @Inject private KubeCertsResource kubeCertsResource; @Inject private ProjectCertsResource projectCertsResource; @@ -99,11 +97,6 @@ public AppCertsResource getAppCertsResource() { return appCertsResource; } - @Path("/dela") - public DelaTrackerCertsResource getDelaTrackerCertsResource() { - return delaTrackerCertsResource; - } - @Path("/kube") public KubeCertsResource getKubeCertsResource() { return kubeCertsResource; diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/DelaTrackerCertsResource.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/DelaTrackerCertsResource.java deleted file mode 100644 index e3188a4f01..0000000000 --- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/api/certificates/DelaTrackerCertsResource.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.ca.api.certificates; - -import io.hops.hopsworks.ca.api.filter.Audience; -import io.hops.hopsworks.ca.api.filter.NoCacheResponse; -import io.hops.hopsworks.ca.controllers.CAException; -import io.hops.hopsworks.ca.controllers.CAInitializationException; -import io.hops.hopsworks.ca.controllers.PKI; -import io.hops.hopsworks.ca.controllers.PKIUtils; -import io.hops.hopsworks.jwt.annotation.JWTRequired; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import org.apache.commons.lang3.tuple.Pair; -import org.bouncycastle.operator.OperatorCreationException; - -import javax.ejb.EJB; -import javax.enterprise.context.RequestScoped; -import javax.naming.InvalidNameException; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.POST; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.cert.X509Certificate; - -import static io.hops.hopsworks.ca.controllers.CertificateType.DELA; - -@RequestScoped -@Api(value = "Dela Tracker certificate service", description = "Manage Dela tracker certificates") -public class DelaTrackerCertsResource { - - @EJB - private NoCacheResponse noCacheResponse; - @EJB - private PKIUtils pkiUtils; - @EJB - private PKI pki; - - @ApiOperation(value = "Sign Dela certificate with IntermediateHopsCA", response = CSRView.class) - @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - @JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"}) - public Response signCSR(@Context SecurityContext sc, CSRView csrView) - throws CAException { - - if (csrView == null || csrView.getCsr() == null || csrView.getCsr().isEmpty()) { - throw new IllegalArgumentException("Empty CSR"); - } - - try { - X509Certificate signedCert = pki.signCertificateSigningRequest(csrView.getCsr(), DELA); - String stringifiedCert = pkiUtils.convertToPEM(signedCert); - Pair chainOfTrust = pki.getChainOfTrust(pkiUtils.getResponsibleCA(DELA)); - CSRView signedCsr = new CSRView(stringifiedCert, chainOfTrust.getLeft(), chainOfTrust.getRight()); - GenericEntity csrViewGenericEntity = new GenericEntity(signedCsr) { }; - return noCacheResponse.getNoCacheResponseBuilder(Response.Status.OK).entity(csrViewGenericEntity).build(); - } catch (IOException | GeneralSecurityException | OperatorCreationException | CAInitializationException ex) { - throw pkiUtils.csrSigningExceptionConvertToCAException(ex, DELA); - } - } - - @ApiOperation(value = "Revoke Dela certificate") - @DELETE - @JWTRequired(acceptedTokens={Audience.SERVICES}, allowedUserRoles={"AGENT"}) - public Response revokeCertificate( - @ApiParam(value = "Identifier of the certificate to revoke", required = true) @QueryParam("certId") String certId) - throws CAException { - if (certId == null || certId.isEmpty()) { - throw new IllegalArgumentException("Empty certificate identifier"); - } - - try { - pki.revokeCertificate(certId, DELA); - return Response.ok().build(); - } catch (InvalidNameException | GeneralSecurityException | CAInitializationException ex) { - throw pkiUtils.certificateRevocationExceptionConvertToCAException(ex, DELA); - } - } -} diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/CertificateType.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/CertificateType.java index a73131ec70..7c29e4ddcb 100644 --- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/CertificateType.java +++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/CertificateType.java @@ -43,8 +43,7 @@ public enum CertificateType { PROJECT("Project"), HOST("Host"), APP("App"), - KUBE("Kube"), - DELA("Dela"); + KUBE("Kube"); private final String certType; diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/PKI.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/PKI.java index dc681b94c1..d3052711d4 100644 --- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/PKI.java +++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/PKI.java @@ -745,7 +745,6 @@ private X509Certificate getCACertificate(CertificateType certificateType) throws case APP: case PROJECT: case HOST: - case DELA: caType = CAType.INTERMEDIATE; break; case KUBE: diff --git a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/PKIUtils.java b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/PKIUtils.java index cd40b8e729..641b0cadb5 100644 --- a/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/PKIUtils.java +++ b/hopsworks-ca/src/main/java/io/hops/hopsworks/ca/controllers/PKIUtils.java @@ -131,7 +131,6 @@ public TemporalAmount getValidityPeriod(CertificateType type) { return getAppCertificateValidityPeriod(); case HOST: return getServiceCertificateValidityPeriod(); - case DELA: case KUBE: case PROJECT: return Duration.ofSeconds(TimeUnit.SECONDS.convert(TEN_YEARS, TimeUnit.DAYS)); @@ -171,7 +170,7 @@ protected long getCertificateValidityInS(String rawConfigurationProperty) { */ public CAType getResponsibleCA(CertificateType certType) { switch (certType) { - case HOST: case DELA: case APP: case PROJECT: + case HOST: case APP: case PROJECT: return CAType.INTERMEDIATE; case KUBE: return CAType.KUBECA; diff --git a/hopsworks-cluster/pom.xml b/hopsworks-cluster/pom.xml deleted file mode 100644 index 0e884b3112..0000000000 --- a/hopsworks-cluster/pom.xml +++ /dev/null @@ -1,145 +0,0 @@ - - - - - 4.0.0 - - io.hops - hopsworks - 3.1.0-SNAPSHOT - ../pom.xml - - - io.hops.hopsworks - hopsworks-cluster - 3.1.0-SNAPSHOT - war - Hopsworks cluster registration api - hopsworks-cluster - - - ${project.build.directory}/endorsed - UTF-8 - true - - - - - - io.hops.hopsworks - hopsworks-persistence - - - - io.hops.hopsworks - hopsworks-rest-utils - - - - io.hops.hopsworks - hopsworks-common - ejb - provided - - - - io.swagger - swagger-jersey2-jaxrs - - - javax - javaee-web-api - - - - - hopsworks-cluster - - - org.apache.maven.plugins - maven-compiler-plugin - - - - - org.apache.maven.plugins - maven-war-plugin - - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - - exec-npm-install - generate-sources - - npm - - install - - ${basedir}/src/main/webapp - - - exec - - - - npm run clean (clean) - - exec - - clean - - npm - - run - clean - - ${basedir}/src/main/webapp - - - - - - - - - diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/Cluster.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/Cluster.java deleted file mode 100644 index 438e9ef2f1..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/Cluster.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.cluster; - -import io.hops.hopsworks.cluster.controller.ClusterController; -import io.hops.hopsworks.persistence.entity.user.cluster.ClusterCert; -import io.hops.hopsworks.common.security.CSR; -import io.hops.hopsworks.cluster.controller.DelaTrackerCertController; -import io.hops.hopsworks.common.security.CertificatesController; -import io.hops.hopsworks.exceptions.DelaCSRCheckException; -import io.hops.hopsworks.exceptions.GenericException; -import io.hops.hopsworks.exceptions.HopsSecurityException; -import io.hops.hopsworks.exceptions.UserException; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiParam; - -import javax.annotation.security.RolesAllowed; -import javax.ejb.EJB; -import javax.mail.MessagingException; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.io.IOException; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Path("cluster") -@Api(value = "Cluster registration Service", - description = "Cluster registration Service") -public class Cluster { - - private static final Logger LOGGER = Logger.getLogger(Cluster.class.getName()); - @EJB - private ClusterController clusterController; - @EJB - private ClusterState clusterState; - @EJB - private DelaTrackerCertController delaTrackerCertController; - @EJB - private CertificatesController certificatesController; - - @POST - @Path("register") - @Consumes(MediaType.APPLICATION_JSON) - public Response register(ClusterDTO cluster, @Context HttpServletRequest req) throws MessagingException, - UserException { - LOGGER.log(Level.INFO, "Registering : {0}", cluster.getEmail()); - boolean autoValidate = clusterState.bypassActivationLink(); - clusterController.registerClusterNewUser(cluster, req, autoValidate); - ClusterJsonResponse res = new ClusterJsonResponse(); - res.setSuccessMessage("Cluster registerd. Please validate your email within " - + ClusterController.VALIDATION_KEY_EXPIRY_DATE + " hours before installing your new cluster."); - return Response.ok().entity(res).build(); - } - - @POST - @Path("register/existing") - @Consumes(MediaType.APPLICATION_JSON) - public Response registerExisting(ClusterDTO cluster, @Context HttpServletRequest req) - throws MessagingException, UserException { - LOGGER.log(Level.INFO, "Registering : {0}", cluster.getEmail()); - boolean autoValidate = clusterState.bypassActivationLink(); - clusterController.registerClusterWithUser(cluster, req, autoValidate); - ClusterJsonResponse res = new ClusterJsonResponse(); - res.setSuccessMessage("Cluster registerd. Please validate your email within " - + ClusterController.VALIDATION_KEY_EXPIRY_DATE + " hours before installing your new cluster."); - return Response.ok().entity(res).build(); - } - - @POST - @Path("unregister") - @Consumes(MediaType.APPLICATION_JSON) - public Response unregister(ClusterDTO cluster, @Context HttpServletRequest req) - throws MessagingException, UserException { - LOGGER.log(Level.INFO, "Unregistering : {0}", cluster.getEmail()); - clusterController.unregister(cluster, req); - ClusterJsonResponse res = new ClusterJsonResponse(); - res.setSuccessMessage("Cluster unregisterd. Please validate your email within " - + ClusterController.VALIDATION_KEY_EXPIRY_DATE + " hours to complite the unregistration."); - return Response.ok().entity(res).build(); - } - - @GET - @Path("register/confirm/{validationKey}") - public Response confirmRegister(@PathParam("validationKey") String validationKey, @Context HttpServletRequest req) - throws HopsSecurityException, GenericException { - ClusterJsonResponse res = new ClusterJsonResponse(); - clusterController.validateRequest(validationKey, req, ClusterController.OP_TYPE.REGISTER); - res.setSuccessMessage("Cluster registration validated."); - return Response.ok().entity(res).build(); - } - - @GET - @Path("unregister/confirm/{validationKey}") - public Response confirmUnregister(@PathParam("validationKey") String validationKey, @Context HttpServletRequest req) - throws HopsSecurityException, GenericException { - ClusterJsonResponse res = new ClusterJsonResponse(); - clusterController.validateRequest(validationKey, req, ClusterController.OP_TYPE.UNREGISTER); - res.setSuccessMessage("Cluster unregistration validated."); - return Response.ok().entity(res).build(); - } - - @POST - @Path("all") - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - public Response getRegisterdClusters(@FormParam("email") String email, @FormParam("pwd") String pwd, - @Context HttpServletRequest req) throws UserException { - ClusterDTO cluster = new ClusterDTO(); - cluster.setEmail(email); - cluster.setChosenPassword(pwd); - List clusters = clusterController.getAllClusterYml(cluster, req); - GenericEntity> clustersEntity = new GenericEntity>(clusters) { - }; - return Response.ok().entity(clustersEntity).build(); - } - - @POST - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - public Response getRegisterdCluster(@FormParam("email") String email, @FormParam("pwd") String pwd, @FormParam( - "orgName") String organizationName, @FormParam("orgUnitName") String organizationalUnitName, - @Context HttpServletRequest req) throws UserException { - ClusterDTO cluster = new ClusterDTO(); - cluster.setEmail(email); - cluster.setChosenPassword(pwd); - cluster.setOrganizationName(organizationName); - cluster.setOrganizationalUnitName(organizationalUnitName); - ClusterCert clusters = clusterController.getCluster(cluster, req); - return Response.ok().entity(clusters).build(); - } - - @POST - @Path("certificate") - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - @RolesAllowed({"CLUSTER_AGENT"}) - public Response singCertificate(CSR csr, @Context HttpServletRequest req) - throws HopsSecurityException, GenericException, DelaCSRCheckException, IOException { - String userEmail = req.getUserPrincipal().getName(); - CSR signedCSR = delaTrackerCertController.signCsr(userEmail, csr); - return Response.ok().entity(signedCSR).build(); - } - - @DELETE - @Path("certificate") - @RolesAllowed({"CLUSTER_AGENT"}) - public Response revokeCertificate(@ApiParam(value = "Identifier of the certificate to revoke", required = true) - @QueryParam("certId") String certId) - throws GenericException, HopsSecurityException{ - - if (certId == null || certId.isEmpty()) { - throw new IllegalArgumentException("Empty certificate identifier"); - } - - certificatesController.revokeDelaClusterCertificate(certId); - return Response.ok().build(); - } -} diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterDTO.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterDTO.java deleted file mode 100644 index 1678228658..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterDTO.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.cluster; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class ClusterDTO { - private String email; - private String chosenPassword; - private String repeatedPassword; - private String organizationName; - private String organizationalUnitName; - private boolean tos; - - public ClusterDTO() { - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getChosenPassword() { - return chosenPassword; - } - - public void setChosenPassword(String chosenPassword) { - this.chosenPassword = chosenPassword; - } - - public String getRepeatedPassword() { - return repeatedPassword; - } - - public void setRepeatedPassword(String repeatedPassword) { - this.repeatedPassword = repeatedPassword; - } - - public String getOrganizationName() { - return organizationName; - } - - public void setOrganizationName(String organizationName) { - this.organizationName = organizationName; - } - - public String getOrganizationalUnitName() { - return organizationalUnitName; - } - - public void setOrganizationalUnitName(String organizationalUnitName) { - this.organizationalUnitName = organizationalUnitName; - } - - public boolean isTos() { - return tos; - } - - public void setTos(boolean tos) { - this.tos = tos; - } - - @Override - public String toString() { - return "ClusterDTO{" + "email=" + email + ", chosenPassword=" + chosenPassword + ", repeatedPassword=" + - repeatedPassword + ", organizationName=" + organizationName + ", organizationalUnitName=" + - organizationalUnitName + ", tos=" + tos + '}'; - } - -} diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterJsonResponse.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterJsonResponse.java deleted file mode 100644 index 32e4ab7407..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterJsonResponse.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.cluster; - -import io.hops.hopsworks.restutils.JsonResponse; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class ClusterJsonResponse extends JsonResponse { - -} - diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterState.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterState.java deleted file mode 100644 index 55ef0da0ce..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterState.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.cluster; - -import java.util.logging.Logger; -import javax.ejb.Singleton; -import javax.ejb.Startup; - -@Startup -@Singleton -public class ClusterState { - private final static Logger LOG = Logger.getLogger(ClusterState.class.getName()); - - //TODO Alex - make it read this from the variables tables - public static final boolean bypassActivationLink = true; - - public boolean bypassActivationLink() { - return bypassActivationLink; - } -} diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterYmlDTO.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterYmlDTO.java deleted file mode 100644 index 1366aff975..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/ClusterYmlDTO.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.cluster; - -import io.hops.hopsworks.persistence.entity.user.cluster.RegistrationStatusEnum; - -import javax.xml.bind.annotation.XmlRootElement; -import java.util.Date; - -@XmlRootElement -public class ClusterYmlDTO { - private String email; - private String commonName; - private String organizationName; - private String organizationalUnitName; - private RegistrationStatusEnum registrationStatus; - private Date registrationDate; - private String serialNumber; - - public ClusterYmlDTO() { - } - - public ClusterYmlDTO(String email, String commonName, String organizationName, String organizationalUnitName, - RegistrationStatusEnum registrationStatus, Date registrationDate, String serialNumber) { - this.email = email; - this.commonName = commonName; - this.organizationName = organizationName; - this.organizationalUnitName = organizationalUnitName; - this.registrationStatus = registrationStatus; - this.registrationDate = registrationDate; - this.serialNumber = serialNumber; - } - - - public String getCommonName() { - return commonName; - } - - public void setCommonName(String commonName) { - this.commonName = commonName; - } - - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getOrganizationName() { - return organizationName; - } - - public void setOrganizationName(String organizationName) { - this.organizationName = organizationName; - } - - public String getOrganizationalUnitName() { - return organizationalUnitName; - } - - public void setOrganizationalUnitName(String organizationalUnitName) { - this.organizationalUnitName = organizationalUnitName; - } - - public RegistrationStatusEnum getRegistrationStatus() { - return registrationStatus; - } - - public void setRegistrationStatus(RegistrationStatusEnum registrationStatus) { - this.registrationStatus = registrationStatus; - } - - public Date getRegistrationDate() { - return registrationDate; - } - - public void setRegistrationDate(Date registrationDate) { - this.registrationDate = registrationDate; - } - - public String getSerialNumber() { - return serialNumber; - } - - public void setSerialNumber(String serialNumber) { - this.serialNumber = serialNumber; - } - - -} diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/Cleanup.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/Cleanup.java deleted file mode 100644 index c2bc221c7c..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/Cleanup.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.cluster.controller; - -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.annotation.Resource; -import javax.ejb.EJB; -import javax.ejb.Singleton; -import javax.ejb.Startup; -import javax.ejb.Timeout; -import javax.ejb.Timer; -import javax.ejb.TimerService; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; - -@Startup -@Singleton -public class Cleanup { - private final static Logger LOG = Logger.getLogger(Cleanup.class.getName()); - @EJB - private ClusterController clusterController; - - @Resource - TimerService timerService; - - @PostConstruct - private void init() { - timerService.createTimer(0, ClusterController.VALIDATION_KEY_EXPIRY_DATE_MS, "Timer for cluster agent cleanup."); - } - - @PreDestroy - private void destroyTimer() { - for (Timer timer : timerService.getTimers()) { - timer.cancel(); - } - } - - @Timeout - @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) - private void timeout(Timer timer) { - try { - clusterController.cleanupUnverifiedUsers(); - } catch(Exception e) { - LOG.log(Level.WARNING, "Failed to cleanup cluster agents. {0}", e.getMessage()); - } - } -} diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/ClusterController.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/ClusterController.java deleted file mode 100644 index 0fd6518d7c..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/ClusterController.java +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.cluster.controller; - -import io.hops.hopsworks.cluster.ClusterDTO; -import io.hops.hopsworks.cluster.ClusterYmlDTO; -import io.hops.hopsworks.common.dao.user.BbcGroupFacade; -import io.hops.hopsworks.common.dao.user.UserFacade; -import io.hops.hopsworks.common.dao.user.cluster.ClusterCertFacade; -import io.hops.hopsworks.common.dao.user.security.ua.UserAccountsEmailMessages; -import io.hops.hopsworks.common.security.CertificatesController; -import io.hops.hopsworks.common.security.utils.SecurityUtils; -import io.hops.hopsworks.common.user.AuthController; -import io.hops.hopsworks.common.user.UsersController; -import io.hops.hopsworks.common.util.EmailBean; -import io.hops.hopsworks.exceptions.GenericException; -import io.hops.hopsworks.exceptions.HopsSecurityException; -import io.hops.hopsworks.exceptions.UserException; -import io.hops.hopsworks.persistence.entity.user.BbcGroup; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.persistence.entity.user.cluster.ClusterCert; -import io.hops.hopsworks.persistence.entity.user.cluster.RegistrationStatusEnum; -import io.hops.hopsworks.persistence.entity.user.security.ua.UserAccountStatus; -import io.hops.hopsworks.persistence.entity.util.FormatUtils; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.servlet.http.HttpServletRequest; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Stateless -@TransactionAttribute(TransactionAttributeType.NEVER) -public class ClusterController { - - private static final Logger LOGGER = Logger.getLogger(ClusterController.class.getName()); - private static final String CLUSTER_NAME_PREFIX = "Agent"; - private static final String CLUSTER_GROUP = "CLUSTER_AGENT"; - public static final long VALIDATION_KEY_EXPIRY_DATE = 48l;//hours to validate request - static final long VALIDATION_KEY_EXPIRY_DATE_MS = 48l * 36l * 100000l;//millisecond to validate request - private static final int VALIDATION_KEY_LEN = 64; - - public enum OP_TYPE { - - REGISTER, - UNREGISTER - } - @EJB - private UserFacade userBean; - @EJB - private ClusterCertFacade clusterCertFacade; - @EJB - private BbcGroupFacade groupFacade; - @EJB - private EmailBean emailBean; - @EJB - private AuthController authController; - @EJB - private UsersController usersCtrl; - @EJB - private CertificatesController certificatesController; - @EJB - private SecurityUtils securityUtils; - - public void registerClusterNewUser(ClusterDTO cluster, HttpServletRequest req, boolean autoValidate) - throws MessagingException, UserException { - isValidNewCluster(cluster); - Users clusterAgent = createClusterAgent(cluster, req); - ClusterCert clusterCert = createClusterCert(cluster, clusterAgent); - - if (autoValidate) { - activateClusterAgent(cluster); - autoActivateCluster(clusterCert); - } else { - sendEmail(cluster, req, clusterCert.getId() + clusterCert.getValidationKey(), clusterAgent, - "REGISTRATION"); - } - LOGGER.log(Level.INFO, "New cluster added with email: {0}, and username: {1}", new Object[]{clusterAgent.getEmail(), - clusterAgent.getUsername()}); - } - - public void registerClusterWithUser(ClusterDTO cluster, HttpServletRequest req, boolean autoValidate) - throws MessagingException, UserException { - isValidCluster(cluster); - Optional clusterAgent = verifyClusterAgent(cluster, req); - if (!clusterAgent.isPresent()) { - throw new IllegalArgumentException("User not registerd."); - } - ClusterCert clusterCert = createClusterCert(cluster, clusterAgent.get()); - if (autoValidate) { - autoActivateCluster(clusterCert); - } else { - sendEmail(cluster, req, clusterCert.getId() + clusterCert.getValidationKey(), clusterAgent.get(), - "REGISTRATION"); - } - LOGGER.log(Level.INFO, "New cluster added with email: {0}, and username: {1}", - new Object[]{clusterAgent.get().getEmail(), clusterAgent.get().getUsername()}); - } - - private Optional verifyClusterAgent(ClusterDTO cluster, HttpServletRequest req) throws UserException { - Users clusterAgent = userBean.findByEmail(cluster.getEmail()); - if (clusterAgent == null) { - return Optional.empty(); - } - checkUserPasswordAndStatus(cluster, clusterAgent, req); - return Optional.of(clusterAgent); - } - - private Users createClusterAgent(ClusterDTO cluster, HttpServletRequest req) throws UserException { - Optional clusterAgentAux = verifyClusterAgent(cluster, req); - if (clusterAgentAux.isPresent()) { - return clusterAgentAux.get(); - } - Users clusterAgent = usersCtrl.createNewAgent(cluster.getEmail(), CLUSTER_NAME_PREFIX, "007", - cluster.getChosenPassword(), "Mrs"); - clusterAgent.setBbcGroupCollection(bbcGroups()); - userBean.persist(clusterAgent); - return clusterAgent; - } - - private void activateClusterAgent(ClusterDTO cluster) { - Users clusterAgent = userBean.findByEmail(cluster.getEmail()); - if (clusterAgent == null) { - throw new IllegalArgumentException("Cluster not registerd."); - } - if (clusterAgent.getStatus().equals(UserAccountStatus.ACTIVATED_ACCOUNT)) { - return; - } - if (clusterAgent.getStatus().equals(UserAccountStatus.NEW_MOBILE_ACCOUNT)) { - clusterAgent.setStatus(UserAccountStatus.ACTIVATED_ACCOUNT); - userBean.update(clusterAgent); - return; - } - throw new IllegalStateException("Trying to activate account in state:" + clusterAgent.getStatus()); - } - - private List bbcGroups() { - BbcGroup group = groupFacade.findByGroupName(CLUSTER_GROUP); - Integer gid = groupFacade.lastGroupID() + 1; - if (group == null) { - group = new BbcGroup(gid, CLUSTER_GROUP);//do this in chef? - group.setGroupDesc("Clusters outside the system"); - groupFacade.save(group); - } - List groups = new ArrayList<>(); - groups.add(group); - return groups; - } - - private ClusterCert createClusterCert(ClusterDTO cluster, Users clusterAgent) { - ClusterCert clusterCert = clusterCertFacade.getByOrgUnitNameAndOrgName(cluster.getOrganizationName(), cluster. - getOrganizationalUnitName()); - if (clusterCert != null) { - throw new IllegalArgumentException( - "Cluster with the same Organization and Organization unit name already registered."); - } - String commonName = cluster.getOrganizationName() + "_" + cluster.getOrganizationalUnitName(); - clusterCert = new ClusterCert(commonName, cluster.getOrganizationName(), cluster.getOrganizationalUnitName(), - RegistrationStatusEnum.REGISTRATION_PENDING, clusterAgent); - clusterCert.setValidationKey(securityUtils.generateSecureRandomString(VALIDATION_KEY_LEN)); - clusterCert.setValidationKeyDate(new Date()); - clusterCertFacade.save(clusterCert); - return clusterCert; - } - - private void autoActivateCluster(ClusterCert clusterCert) { - clusterCert.setRegistrationStatus(RegistrationStatusEnum.REGISTERED); - clusterCert.setValidationKey(null); - clusterCert.setValidationKeyDate(null); - clusterCertFacade.update(clusterCert); - } - - public void unregister(ClusterDTO cluster, HttpServletRequest req) throws MessagingException, UserException { - isValidCluster(cluster); - Users clusterAgent = userBean.findByEmail(cluster.getEmail()); - if (clusterAgent == null) { - throw new IllegalArgumentException("Cluster not registerd."); - } - ClusterCert clusterCert = clusterCertFacade.getByOrgUnitNameAndOrgName(cluster.getOrganizationName(), cluster. - getOrganizationalUnitName()); - if (clusterCert == null) { - throw new IllegalArgumentException("Cluster not registerd."); - } - if (clusterCert.getRegistrationStatus().equals(RegistrationStatusEnum.UNREGISTRATION_PENDING) && getDateDiffHours( - clusterCert.getValidationKeyDate()) < VALIDATION_KEY_EXPIRY_DATE) { - throw new IllegalArgumentException( - "Cluster unregisterd use the validation key sent to you via email to complete unregistration."); - } - if (!isOnlyClusterAgent(clusterAgent)) { - throw new IllegalArgumentException("Not a cluster agent."); - } - checkUserPasswordAndStatus(cluster, clusterAgent, req); - - clusterCert.setValidationKey(securityUtils.generateSecureRandomString(VALIDATION_KEY_LEN)); - clusterCert.setRegistrationStatus(RegistrationStatusEnum.UNREGISTRATION_PENDING); - clusterCert.setValidationKeyDate(new Date()); - clusterCertFacade.update(clusterCert); - sendEmail(cluster, req, clusterCert.getId() + clusterCert.getValidationKey(), clusterAgent, - "UNREGISTRATION"); - LOGGER.log(Level.INFO, "Unregistering cluster with email: {0}", clusterAgent.getEmail()); - } - - public void validateRequest(String key, HttpServletRequest req, OP_TYPE type) - throws HopsSecurityException, GenericException { - - Integer clusterCertId = extractClusterCertId(key); - ClusterCert clusterCert = clusterCertFacade.find(clusterCertId); - if (clusterCert == null) { - throw new IllegalStateException("Agent not found."); - } - long diff = getDateDiffHours(clusterCert.getValidationKeyDate()); - String validationKey = extractValidationKey(key); - Users agent = clusterCert.getAgentId(); - if (agent == null) { - throw new IllegalStateException("Agent not found."); - } - if (!validationKey.equals(clusterCert.getValidationKey())) { - throw new IllegalStateException("Validation key not found."); - } - if (diff > VALIDATION_KEY_EXPIRY_DATE) { - removeUserIfNotValidated(agent); - throw new IllegalStateException("Expired valdation key."); - } - if (type.equals(OP_TYPE.REGISTER) && clusterCert.getRegistrationStatus().equals( - RegistrationStatusEnum.REGISTRATION_PENDING)) { - if (agent.getStatus() == UserAccountStatus.NEW_MOBILE_ACCOUNT) { - agent.setStatus(UserAccountStatus.ACTIVATED_ACCOUNT); - userBean.update(agent); - } - clusterCert.setValidationKey(null); - clusterCert.setValidationKeyDate(null); - clusterCert.setRegistrationStatus(RegistrationStatusEnum.REGISTERED); - clusterCertFacade.update(clusterCert); - } else if (clusterCert.getRegistrationStatus().equals(RegistrationStatusEnum.UNREGISTRATION_PENDING)) { - revokeCert(clusterCert); - removeClusterCert(clusterCert); - } - } - - private void removeClusterCert(ClusterCert clusterCert) { - List clusterCerts = clusterCertFacade.getByAgent(clusterCert.getAgentId()); - if (clusterCerts.size() > 1) { - clusterCertFacade.remove(clusterCert); - LOGGER.log(Level.INFO, "Removed cluster {0} for user: {1}", new Object[]{clusterCert.getCommonName(), clusterCert. - getAgentId().getEmail()}); - return; - } - LOGGER.log(Level.INFO, "Removing user: {0}", clusterCert.getAgentId().getEmail()); - userBean.removeByEmail(clusterCert.getAgentId().getEmail()); - } - - public void cleanupUnverifiedUsers() { - BbcGroup group = groupFacade.findByGroupName(CLUSTER_GROUP); - if (group == null) { - return; - } - List usersInGroup = userBean.findAllInGroup(group.getGid()); - Users u; - for (Integer uid : usersInGroup) { - u = userBean.find(uid); - removeUserIfNotValidated(u); - } - } - - public List getAllClusters(ClusterDTO cluster, HttpServletRequest req) throws UserException { - if (cluster == null) { - throw new NullPointerException("Cluster not assigned."); - } - if (cluster.getEmail() == null || cluster.getEmail().isEmpty()) { - throw new IllegalArgumentException("Cluster email not set."); - } - if (cluster.getChosenPassword() == null || cluster.getChosenPassword().isEmpty()) { - throw new IllegalArgumentException("Cluster password not set."); - } - Users clusterAgent = userBean.findByEmail(cluster.getEmail()); - if (clusterAgent == null) { - throw new IllegalArgumentException("No registerd cluster found for user."); - } - checkUserPasswordAndStatus(cluster, clusterAgent, req); - return clusterCertFacade.getByAgent(clusterAgent); - } - - public List getAllClusterYml(ClusterDTO cluster, HttpServletRequest req) throws UserException { - if (cluster == null) { - throw new NullPointerException("Cluster not assigned."); - } - if (cluster.getEmail() == null || cluster.getEmail().isEmpty()) { - throw new IllegalArgumentException("Cluster email not set."); - } - if (cluster.getChosenPassword() == null || cluster.getChosenPassword().isEmpty()) { - throw new IllegalArgumentException("Cluster password not set."); - } - Users clusterAgent = userBean.findByEmail(cluster.getEmail()); - if (clusterAgent == null) { - throw new IllegalArgumentException("No registerd cluster found for user."); - } - checkUserPasswordAndStatus(cluster, clusterAgent, req); - List clusterCerts = clusterCertFacade.getByAgent(clusterAgent); - List clusterYmlDTOs = new ArrayList<>(); - for (ClusterCert cCert : clusterCerts) { - clusterYmlDTOs.add(new ClusterYmlDTO(cCert.getAgentId().getEmail(), - cCert.getCommonName(), - cCert.getOrganizationName(), - cCert.getOrganizationalUnitName(), - cCert.getRegistrationStatus(), - cCert.getRegistrationDate(), - cCert.getSerialNumber())); - } - return clusterYmlDTOs; - } - - public ClusterCert getCluster(ClusterDTO cluster, HttpServletRequest req) throws UserException { - - isValidCluster(cluster); - Users clusterAgent = userBean.findByEmail(cluster.getEmail()); - if (clusterAgent == null) { - throw new IllegalArgumentException("Cluster not registerd."); - } - checkUserPasswordAndStatus(cluster, clusterAgent, req); - ClusterCert clusterCert = clusterCertFacade.getByOrgUnitNameAndOrgName(cluster.getOrganizationName(), cluster. - getOrganizationalUnitName()); - if (clusterCert == null) { - throw new IllegalArgumentException("Cluster not registerd."); - } - return clusterCert; - } - - private void checkUserPasswordAndStatus(ClusterDTO cluster, Users clusterAgent, HttpServletRequest req) - throws UserException { - authController.checkPasswordAndStatus(clusterAgent, cluster.getChosenPassword()); - BbcGroup group = groupFacade.findByGroupName(CLUSTER_GROUP); - if (!clusterAgent.getBbcGroupCollection().contains(group)) { - throw new SecurityException("User not allowed to register clusters."); - } - } - - private void removeUserIfNotValidated(Users u) { - if (u == null) { - return; - } - if (!isOnlyClusterAgent(u)) { - return; - } - List clusterCerts = clusterCertFacade.getByAgent(u); - long diff; - int countExpired = 0; - for (ClusterCert clusterCert : clusterCerts) { - Date validationKeyDate = clusterCert.getValidationKeyDate(); - if (validationKeyDate == null) { - continue; - } - diff = getDateDiffHours(validationKeyDate); - if (diff > VALIDATION_KEY_EXPIRY_DATE && clusterCert.getRegistrationStatus().equals( - RegistrationStatusEnum.REGISTRATION_PENDING)) { - countExpired++; - clusterCertFacade.remove(clusterCert); - } else if (diff > VALIDATION_KEY_EXPIRY_DATE && clusterCert.getRegistrationStatus().equals( - RegistrationStatusEnum.UNREGISTRATION_PENDING)) { - clusterCert.setRegistrationStatus(RegistrationStatusEnum.REGISTERED); - clusterCert.setValidationKeyDate(null); - clusterCertFacade.update(clusterCert); - } - } - if (countExpired == clusterCerts.size()) { - userBean.removeByEmail(u.getEmail()); - } - } - - private boolean isOnlyClusterAgent(Users u) { - BbcGroup group = groupFacade.findByGroupName(CLUSTER_GROUP); - boolean isInClusterAgent = u.getBbcGroupCollection().contains(group); - return u.getBbcGroupCollection().size() == 1 && isInClusterAgent; - } - - private void isValidNewCluster(ClusterDTO cluster) { - isValidCluster(cluster); - if (!cluster.getChosenPassword().equals(cluster.getRepeatedPassword())) { - throw new IllegalArgumentException("Cluster password does not match."); - } - if (!cluster.isTos()) { - throw new IllegalStateException("You should agree with the terms and conditions."); - } - } - - private void isValidCluster(ClusterDTO cluster) { - if (cluster == null) { - throw new NullPointerException("Cluster not assigned."); - } - if (cluster.getEmail() == null || cluster.getEmail().isEmpty()) { - throw new IllegalArgumentException("Cluster email not set."); - } - if (cluster.getChosenPassword() == null || cluster.getChosenPassword().isEmpty()) { - throw new IllegalArgumentException("Cluster password not set."); - } - if (cluster.getOrganizationName() == null || cluster.getOrganizationName().isEmpty()) { - throw new IllegalArgumentException("Cluster Organization Name not set."); - } - if (cluster.getOrganizationalUnitName() == null || cluster.getOrganizationalUnitName().isEmpty()) { - throw new IllegalArgumentException("Cluster Organizational Unit Name not set."); - } - } - - private void sendEmail(ClusterDTO cluster, HttpServletRequest req, String validationKey, Users u, String type) throws - MessagingException { - if (type == null || type.isEmpty()) { - throw new IllegalArgumentException("No type set."); - } - try { - if (type.equals("REGISTRATION")) { - emailBean.sendEmail(cluster.getEmail(), Message.RecipientType.TO, - UserAccountsEmailMessages.CLUSTER_REQUEST_SUBJECT, UserAccountsEmailMessages. - buildClusterRegisterRequestMessage(FormatUtils.getUserURL(req), validationKey)); - } else { - emailBean.sendEmail(cluster.getEmail(), Message.RecipientType.TO, - UserAccountsEmailMessages.CLUSTER_REQUEST_SUBJECT, UserAccountsEmailMessages. - buildClusterUnregisterRequestMessage(FormatUtils.getUserURL(req), validationKey)); - } - } catch (MessagingException ex) { - LOGGER.log(Level.SEVERE, "Could not send email to ", u.getEmail()); - throw new MessagingException(ex.getMessage()); - } - } - - private Integer extractClusterCertId(String key) { - if (key == null || key.isEmpty() || key.length() <= VALIDATION_KEY_LEN) { - throw new IllegalArgumentException("Key not valid."); - } - int idLen = key.length() - VALIDATION_KEY_LEN; - Integer id; - try { - id = Integer.parseInt(key.substring(0, idLen)); - } catch (NumberFormatException e) { - return null; - } - return id; - } - - private String extractValidationKey(String key) { - if (key == null || key.isEmpty() || key.length() <= VALIDATION_KEY_LEN) { - throw new IllegalArgumentException("Key too short."); - } - int idLen = key.length() - VALIDATION_KEY_LEN; - return key.substring(idLen); - } - - private long getDateDiffHours(Date start) { - Date now = new Date(); - long diff = now.getTime() - start.getTime(); - return TimeUnit.MILLISECONDS.toHours(diff); - } - - private void revokeCert(ClusterCert clusterCert) throws HopsSecurityException, GenericException { - if (clusterCert == null || clusterCert.getSerialNumber() == null) { - return; - } - - certificatesController.revokeDelaClusterCertificate(clusterCert.getCommonName()); - } -} diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/DelaTrackerCertController.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/DelaTrackerCertController.java deleted file mode 100644 index 92d3400642..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/controller/DelaTrackerCertController.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.cluster.controller; - -import io.hops.hopsworks.common.dao.user.UserFacade; -import io.hops.hopsworks.common.dao.user.cluster.ClusterCertFacade; -import io.hops.hopsworks.common.security.CSR; -import io.hops.hopsworks.common.security.CertificatesController; -import io.hops.hopsworks.exceptions.DelaCSRCheckException; -import io.hops.hopsworks.exceptions.GenericException; -import io.hops.hopsworks.exceptions.HopsSecurityException; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.persistence.entity.user.cluster.ClusterCert; -import io.hops.hopsworks.restutils.RESTCodes; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x500.style.BCStyle; -import org.bouncycastle.asn1.x500.style.IETFUtils; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import java.io.IOException; -import java.security.cert.CertificateException; -import java.util.logging.Level; - -import static io.hops.hopsworks.restutils.RESTCodes.DelaCSRErrorCode.AGENTIDNOTFOUND; -import static io.hops.hopsworks.restutils.RESTCodes.DelaCSRErrorCode.CN; -import static io.hops.hopsworks.restutils.RESTCodes.DelaCSRErrorCode.CNNOTFOUND; -import static io.hops.hopsworks.restutils.RESTCodes.DelaCSRErrorCode.EMAIL; -import static io.hops.hopsworks.restutils.RESTCodes.DelaCSRErrorCode.NOTFOUND; -import static io.hops.hopsworks.restutils.RESTCodes.DelaCSRErrorCode.O; -import static io.hops.hopsworks.restutils.RESTCodes.DelaCSRErrorCode.OU; -import static io.hops.hopsworks.restutils.RESTCodes.DelaCSRErrorCode.SERIALNUMBER; - -@Stateless -@TransactionAttribute(TransactionAttributeType.NEVER) -public class DelaTrackerCertController { - - @EJB - private ClusterCertFacade clusterCertFacade; - @EJB - private UserFacade userFacade; - @EJB - private CertificatesController certificatesController; - - public CSR signCsr(String userEmail, CSR csr) - throws IOException, HopsSecurityException, GenericException, DelaCSRCheckException { - ClusterCert clusterCert = checkCSR(userEmail, csr); - - CSR signedCert = certificatesController.signDelaClusterCertificate(csr); - String certSerialNumber; - try { - certSerialNumber = String.valueOf( - certificatesController.extractSerialNumberFromCert(signedCert.getSignedCert())); - } catch (CertificateException e) { - throw new HopsSecurityException(RESTCodes.SecurityErrorCode.CERT_CREATION_ERROR, Level.WARNING, null, null, e); - } - clusterCert.setSerialNumber(certSerialNumber); - clusterCertFacade.update(clusterCert); - - return signedCert; - } - - private ClusterCert checkCSR(String userEmail, CSR csrDTO) throws IOException, DelaCSRCheckException { - Users user = userFacade.findByEmail(userEmail); - String csr = csrDTO.getCsr(); - if (user == null || user.getEmail() == null || csr == null || csr.isEmpty()) { - throw new IllegalArgumentException("User or CSR is empty"); - } - - //subject=/C=se/CN=bbc.sics.se/ST=stockholm/L=kista/O=hopsworks/OU=hs/emailAddress=dela1@kth.se - X500Name subject = certificatesController.extractSubjectFromCSR(csrDTO.getCsr()); - String email = IETFUtils.valueToString(subject.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue()); - String commonName = IETFUtils.valueToString(subject.getRDNs(BCStyle.CN)[0].getFirst().getValue()); - String organizationName = IETFUtils.valueToString(subject.getRDNs(BCStyle.O)[0].getFirst().getValue()); - String organizationalUnitName = IETFUtils.valueToString(subject.getRDNs(BCStyle.OU)[0].getFirst().getValue()); - if (email.isEmpty() || !email.equals(user.getEmail())) { - throw new DelaCSRCheckException(EMAIL, Level.FINE); - } - if (commonName.isEmpty()) { - throw new DelaCSRCheckException(CN, Level.FINE); - } - if (organizationName.isEmpty()) { - throw new DelaCSRCheckException(O, Level.FINE); - } - if (organizationalUnitName.isEmpty()) { - throw new DelaCSRCheckException(OU, Level.FINE); - } - - ClusterCert clusterCert = clusterCertFacade.getByOrgUnitNameAndOrgName(organizationName, organizationalUnitName); - if (clusterCert == null) { - throw new DelaCSRCheckException(NOTFOUND, Level.FINE); - } - if (clusterCert.getSerialNumber() != null && !clusterCert.getSerialNumber().isEmpty()) { - throw new DelaCSRCheckException(SERIALNUMBER, Level.FINE); - } - if (!clusterCert.getCommonName().equals(commonName)) { - throw new DelaCSRCheckException(CNNOTFOUND, Level.FINE); - } - if (!clusterCert.getAgentId().equals(user)) { - throw new DelaCSRCheckException(AGENTIDNOTFOUND, Level.FINE); - } - return clusterCert; - } -} - diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/exception/mapper/ClusterJsonResponse.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/exception/mapper/ClusterJsonResponse.java deleted file mode 100644 index 1ceb8e319a..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/exception/mapper/ClusterJsonResponse.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of Hopsworks - * Copyright (C) 2019, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - */ -package io.hops.hopsworks.cluster.exception.mapper; - -import io.hops.hopsworks.restutils.JsonResponse; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class ClusterJsonResponse extends JsonResponse { -} diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/exception/mapper/ClusterThrowableMapper.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/exception/mapper/ClusterThrowableMapper.java deleted file mode 100644 index feb9b82dc5..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/exception/mapper/ClusterThrowableMapper.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of Hopsworks - * Copyright (C) 2019, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - */ - -package io.hops.hopsworks.cluster.exception.mapper; - -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.restutils.RESTException; -import io.hops.hopsworks.restutils.ThrowableMapper; - -import javax.ejb.EJB; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.Provider; - -@Provider -public class ClusterThrowableMapper extends ThrowableMapper { - - @EJB - private Settings settings; - - @Override - public Response handleRESTException(Response.StatusType status, RESTException ex) { - return Response.status(status) - .entity(ex.buildJsonResponse(new ClusterJsonResponse(), settings.getHopsworksRESTLogLevel())) - .type(MediaType.APPLICATION_JSON) - .build(); - } - -} diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/response/filter/CORSFilter.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/response/filter/CORSFilter.java deleted file mode 100644 index f7642f80a6..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/response/filter/CORSFilter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.cluster.response.filter; - -import java.io.IOException; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; -import javax.ws.rs.ext.Provider; - -@Provider -public class CORSFilter implements ContainerResponseFilter { - - @Override - public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws - IOException { - responseContext.getHeaders().add("Access-Control-Allow-Origin", "*"); - responseContext.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); - responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true"); - responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); - responseContext.getHeaders().add("Access-Control-Max-Age", "1209600"); - } - -} diff --git a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/rest/application/config/ApplicationConfig.java b/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/rest/application/config/ApplicationConfig.java deleted file mode 100644 index c5d3fd48ca..0000000000 --- a/hopsworks-cluster/src/main/java/io/hops/hopsworks/cluster/rest/application/config/ApplicationConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.cluster.rest.application.config; - -import io.hops.hopsworks.cluster.exception.mapper.ClusterThrowableMapper; -import io.swagger.annotations.Api; -import org.glassfish.jersey.server.ResourceConfig; - -@Api -@javax.ws.rs.ApplicationPath("api") -public class ApplicationConfig extends ResourceConfig { - - /** - * adding manually all the restful services of the application. - */ - public ApplicationConfig() { - register(io.hops.hopsworks.cluster.Cluster.class); - register(io.hops.hopsworks.cluster.response.filter.CORSFilter.class); - - register(ClusterThrowableMapper.class); - - //swagger - register(io.swagger.jaxrs.listing.ApiListingResource.class); - register(io.swagger.jaxrs.listing.SwaggerSerializers.class); - } - -} diff --git a/hopsworks-cluster/src/main/webapp/.bowerrc b/hopsworks-cluster/src/main/webapp/.bowerrc deleted file mode 100644 index 9e2cfb4fb5..0000000000 --- a/hopsworks-cluster/src/main/webapp/.bowerrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "directory": "vendor", - "registry": "https://registry.bower.io" -} diff --git a/hopsworks-cluster/src/main/webapp/WEB-INF/glassfish-web.xml b/hopsworks-cluster/src/main/webapp/WEB-INF/glassfish-web.xml deleted file mode 100644 index 43ca3bdf00..0000000000 --- a/hopsworks-cluster/src/main/webapp/WEB-INF/glassfish-web.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - /hopsworks-cluster - - CLUSTER_AGENT - CLUSTER_AGENT - - - diff --git a/hopsworks-cluster/src/main/webapp/WEB-INF/web.xml b/hopsworks-cluster/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index fbbfb38231..0000000000 --- a/hopsworks-cluster/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - Jersey2Config - io.swagger.jersey.config.JerseyJaxrsConfig - - api.version - ${project.version} - - - swagger.api.title - Hopsworks cluster - - - swagger.api.basepath - /hopsworks-cluster/api - - 2 - - - - CLUSTER_AGENT - - - - Viewpoint Secure URLs - /* - - - - BASIC - cauthRealm - - - javax.faces.FACELETS_SKIP_COMMENTS - true - - - 1440 - - SESSION - / - false - false - - - diff --git a/hopsworks-cluster/src/main/webapp/bower.json b/hopsworks-cluster/src/main/webapp/bower.json deleted file mode 100644 index ee17335be1..0000000000 --- a/hopsworks-cluster/src/main/webapp/bower.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "cluster-registration", - "description": "hopsworks cluster registration", - "main": "index.js", - "authors": [ - "ErmiasG" - ], - "license": "Apache-2.0", - "homepage": "https://github.com/AlexHopsworks/hopsworks", - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "src/main/webapp/vendor", - "test", - "tests" - ], - "dependencies": { - "bootstrap": "^3.3.7", - "angular": "1.6.7", - "angular-route": "^1.6.7", - "angular-loader": "^1.6.7", - "angular-mocks": "^1.6.7", - "angular-bootstrap": "^2.5.0", - "components-font-awesome": "^4.7.0" - }, - "resolutions": { - "angular": "1.6.7" - } -} diff --git a/hopsworks-cluster/src/main/webapp/css/main.css b/hopsworks-cluster/src/main/webapp/css/main.css deleted file mode 100644 index 96ebf9b4ab..0000000000 --- a/hopsworks-cluster/src/main/webapp/css/main.css +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -body { - background-image: url("../img/network.png"); - background-color: #8ec3eb; - overflow-x: hidden; -} - -.center { - margin: auto; - width: 50%; - padding: 10px; - text-align: center; - color: #fff; -} - -.alert { - overflow-x: auto !important; -} diff --git a/hopsworks-cluster/src/main/webapp/img/network.png b/hopsworks-cluster/src/main/webapp/img/network.png deleted file mode 100644 index e00cd2d43f7f0bcdf4297b20a9a87e68b7427032..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286103 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 seChG^A z%$yI&rRgD1D3T+;zhX8Vo02!sXlii>7`S%MYEAQcs88UNDaQr zu(cM+RV7JX2Jc2FUvBYdM{P&5U}8<5E3*xAdKJ`GSK1>0JE z{JHXsIzbDTu0R#8|1~M3{odz7u3=j#j446lg`ol+P75zBsG6TiNhQTtj~FZ8zRzqU zA^xo?w$F4Q@q&FCP4Dfj#U9a%_UU|c3@>a8DIW7=zSm8Fbfv)Pi0b^`oFfge3D8j( zE%wscWBF@HsNS;!U`Y-KnAI$tf50+-B0WJBU(~AjgHl1bcFxZ?9w_b~|26MF(MGp9 z%R4LLd2u%%^OX=U$E%cMNl`3k9P^D#L~s*UGeyk}e#QY4%&5XUsv2V7`#GOpU_IqF zuoDu$&d8u_s+8=`nv+Zb7+d|HzZH%PrDu49{6XGW$yY&P)K{1r&m=ca6TjE)#lI-^ zD%xj|{}I}#&hCU|X>ESiaRFwj6w2j5aR;0O zTW+Reoc<^uQNT}ik2d%%UBk%ZlS#nSUuZ1?Ggj`jwU|(j9PD^mLAD@WeEAxSb`25y z6MkKCp7J7SNM5SDXtbc)uhb#G7FHT@w87k70NP9b zc2@i)ySZzj>pK9+oF|6N@(?vV2i`2V0GdM_S#(=hA^2J&Fmc?fW-%Lud{ukX#HhQc zAOQLqSg{sxr<}33VzD0dplkrpwbuc_RMEAxTR_bCz+>kx4qc9B3mQ8$5G1=MEbH27EC6f-q!k>7x8t zO9h2VcV9RN_N#{I;0XcVl#{$vdC4Xu1%b`2{FbHP(@Y(qtV=eyskJkg`V^RQNkRAd zjLAk1t=55=)_X7IY=!muguwR~BcLN*JG=!4fw$JWa=@tx=MFFMtAIw^x{#R!O4 zqFs71hU_xqHC<{!LX=#7HE~6EXmi; z2Lgcy!W}venaPjQRRI>G$;LQFBkw%C$<}q71>q?&z0FgCj>5o@1dLaXlFhpNvPXmK z%R$4uC00Ym(ntw`%|5V^I`OxDu3=<}AwA~36T{`f2S(r?5uMHkni}DAUZ9yBM3dyM-x(Q)UMbK7w@I5!@=w4hQ{&6 zadYqfmEXtYpK$D$Vb^?4+AB>T6$P?J|#?3<#tldgj=RkPGRKN38FS^Z}Jq=$zZKu2=i$<}SVX&g%?-0Bibmrz=u zK^DX5;FEbiOHWLAZUR z7GA#ti#{RoFwL)O^#8=r#8t= zURKu_6emxSF}KsIvr&%HBEm+a!g7!~T?rL}2)r4a+vUN&8ovZ06sT2F{1A^PBICmj zD~`E;j&?#`&-Xi-$iTBSUbKS1`w7K+8Y3L*PlaApI=bOhU7PDU6lXhF#qK+vK3vwS zz6{8#_>^$HzAFTlZmf`-&O1w*W!qgZJW!KbmcbieMEr(%{L=)Z*8?x&=!Ec5-IGHu zIFpYh&>PS1{@mIRnZQ|ae!?VULk`2OgZ;jTUh2uBeeL^OCd36_3KWi2X_4>$CTgre zmws5MNxbNCCn6_*_1qP2YyK(tlaztK4US)n+_v9NbXJTUhP`Uq_`{chk0|*kR*0=k zMzQRu5OEQ6;~!%b3c~u|a>xl8lZ-T3pUUkbWJJ$&$$Q>9<5R~rLoYt2^!tUiYGx-V z1k*I~!}Ql>lAB^|7xIS+zs*j^A2Qer!IkpwW=`+kcChG)^QdA3{_@?w^@PJG=(iC+ zmkf0gtekmn>hv3ai=h`);�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

M=oG`o)!3u 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%0D-zwMBBP8RoPbO{7IX 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

xVMFh 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 - - - - - - Cluster registration - - - - - - -

- - - - - - - - - - - - - diff --git a/hopsworks-cluster/src/main/webapp/js/app.js b/hopsworks-cluster/src/main/webapp/js/app.js deleted file mode 100644 index c888b3a242..0000000000 --- a/hopsworks-cluster/src/main/webapp/js/app.js +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -'use strict'; - - -var app = angular.module('app', [ - 'ngRoute', - 'ui.bootstrap', - 'directives', - 'services', - 'controllers' -]); - - -app.config(['$routeProvider', - function ($routeProvider) { - $routeProvider - .when('/', { - templateUrl: 'partials/home.html', - controller: 'HomeController' - }) - .when('/register', { - templateUrl: 'partials/register.html', - controller: 'RegisterController' - }) - .when('/registerCluster', { - templateUrl: 'partials/registerCluster.html', - controller: 'RegisterController' - }) - .when('/unregister', { - templateUrl: 'partials/unregister.html', - controller: 'UnregisterController' - }) - .when('/view', { - templateUrl: 'partials/viewRegisteredClusters.html', - controller: 'RegisteredClusters' - }) - .otherwise({ - redirectTo: '/' - }); - }]); diff --git a/hopsworks-cluster/src/main/webapp/js/controllers.js b/hopsworks-cluster/src/main/webapp/js/controllers.js deleted file mode 100644 index 268cd29ecc..0000000000 --- a/hopsworks-cluster/src/main/webapp/js/controllers.js +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -'use strict'; - -var controllers = angular.module("controllers", []); - -controllers.controller("HomeController", ['$scope', function ($scope) { - $scope.title = 'Cluster management'; - }]); - -controllers.controller("RegisterController", ['$scope', 'ClusterService', function ($scope, ClusterService) { - $scope.title = 'Register your cluster'; - $scope.working = false; - $scope.successMessage = ''; - $scope.errorMessage = ''; - $scope.newUser = {organizationName: '', - organizationalUnitName: '', - email: '', - chosenPassword: '', - repeatedPassword: '', - tos: ''};//Terms of service - $scope.validationKey = ''; - $scope.register = function () { - if ($scope.newUser.commonName === '' || $scope.newUser.email === '' || - $scope.newUser.organizationName === '' || $scope.newUser.organizationalUnitName === '' || - $scope.newUser.chosenPassword === '' || $scope.newUser.repeatedPassword === '' || - $scope.newUser.chosenPassword !== $scope.newUser.repeatedPassword) { - return; - } - $scope.successMessage = ''; - $scope.errorMessage = ''; - $scope.working = true; - ClusterService.register($scope.newUser).then( - function (success) { - $scope.successMessage = success.data.successMessage; - $scope.working = false; - }, function (error) { - $scope.errorMessage = error.data.errorMsg; - $scope.working = false; - }); - }; - $scope.registerCluster = function () { - if ($scope.newUser.commonName === '' || $scope.newUser.email === '' || - $scope.newUser.organizationName === '' || $scope.newUser.organizationalUnitName === '' || - $scope.newUser.chosenPassword === '') { - return; - } - $scope.successMessage = ''; - $scope.errorMessage = ''; - $scope.working = true; - ClusterService.registerCluster($scope.newUser).then( - function (success) { - $scope.successMessage = success.data.successMessage; - $scope.working = false; - }, function (error) { - $scope.errorMessage = error.data.errorMsg; - $scope.working = false; - }); - }; - $scope.confirmRegister = function () { - if ($scope.validationKey === '' || $scope.validationKey.length < 64) { - $scope.errorMessage = 'Key not set or too short.'; - return; - } - $scope.successMessage = ''; - $scope.errorMessage = ''; - $scope.working = true; - ClusterService.confirmRegister($scope.validationKey).then( - function (success) { - $scope.successMessage = success.data.successMessage; - $scope.working = false; - }, function (error) { - $scope.errorMessage = error.data.errorMsg; - $scope.working = false; - }); - }; - - $scope.$watch('newUser.email', function (newValue, oldValue) { - var orgAndUnit; - var index; - if (newValue !== undefined) { - index = newValue.indexOf("@"); - if (index !== -1) { - orgAndUnit = newValue.split("@"); - if ($scope.registerForm.org_name.$pristine) { - $scope.newUser.organizationName = orgAndUnit[1]; - } - if ($scope.registerForm.org_unit_name.$pristine) { - $scope.newUser.organizationalUnitName = orgAndUnit[0]; - } - } - } - }, true); - - }]); - -controllers.controller("UnregisterController", ['$scope', 'ClusterService', function ($scope, ClusterService) { - $scope.title = 'Unregister your cluster'; - $scope.working = false; - $scope.successMessage = ''; - $scope.errorMessage = ''; - $scope.user = {email: '', - organizationName: '', - organizationalUnitName: '', - chosenPassword: ''}; - $scope.validationKey = ''; - $scope.unregister = function () { - if ($scope.user.email === '' || $scope.user.organizationName === '' || - $scope.user.organizationalUnitName === '' || $scope.user.chosenPassword === '') { - return; - } - $scope.successMessage = ''; - $scope.errorMessage = ''; - $scope.working = true; - ClusterService.unregister($scope.user).then( - function (success) { - $scope.successMessage = success.data.successMessage; - $scope.working = false; - }, function (error) { - $scope.errorMessage = error.data.errorMsg; - $scope.working = false; - }); - }; - - $scope.confirmUnregister = function () { - if ($scope.validationKey === '' || $scope.validationKey.length < 64) { - $scope.errorMessage = 'Key not set or too short.'; - return; - } - $scope.successMessage = ''; - $scope.errorMessage = ''; - $scope.working = true; - ClusterService.confirmUnregister($scope.validationKey).then( - function (success) { - $scope.successMessage = success.data.successMessage; - $scope.working = false; - }, function (error) { - $scope.errorMessage = error.data.errorMsg; - $scope.working = false; - }); - }; - }]); - -controllers.controller("RegisteredClusters", ['$scope', 'ClusterService', function ($scope, ClusterService) { - $scope.title = 'Registered clusters'; - $scope.working = false; - $scope.successMessage = ''; - $scope.errorMessage = ''; - $scope.user = {email: '', - chosenPassword: ''}; - $scope.clusters = undefined; - - $scope.getClusters = function () { - if ($scope.user.chosenPassword === '' || $scope.user.email === '') { - return; - } - $scope.successMessage = ''; - $scope.errorMessage = ''; - $scope.working = true; - ClusterService.getAllClusters($scope.user).then( - function (success) { - $scope.clusters = success.data; - $scope.working = false; - }, function (error) { - $scope.clusters = []; - $scope.errorMessage = error.data.errorMsg; - $scope.working = false; - }); - }; - - $scope.certStatus = function (serial) { - return serial === undefined? "Certificate not yet signed." : "Certificate signed."; - }; - - }]); \ No newline at end of file diff --git a/hopsworks-cluster/src/main/webapp/js/directives.js b/hopsworks-cluster/src/main/webapp/js/directives.js deleted file mode 100644 index d4d99e43d0..0000000000 --- a/hopsworks-cluster/src/main/webapp/js/directives.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -'use strict'; - -var directives = angular.module("directives", []); - -directives.directive('pwCheck', function () { - return { - require: 'ngModel', - link: function (scope, element, attributes, ctrl) { - if (!ctrl) { - if (console && console.warn) { - console.warn('Match validation requires ngModel to be on the element'); - } - return; - } - //get the value of the first password - var firstPwd = '#' + attributes.pwCheck; - var secondPwd = '#' + attributes.id; - var otherElement = $(firstPwd); - var thisElement = $(secondPwd); - var setCustomValidation = function (elem, text) { - if (elem.get(0) && elem.get(0).setCustomValidity) { // HTML5 validation - elem.get(0).setCustomValidity(text); - } else { - ctrl.$setValidity('pwmatch', text === ''); - } - }; - element.on('keyup', function () { - scope.$apply(function () { - var v = element.val() === otherElement.val(); - if (!v) { - setCustomValidation(thisElement, "Passwords must match"); - } else { - setCustomValidation(thisElement, ""); - } - }); - }); - } - }; -}); - - diff --git a/hopsworks-cluster/src/main/webapp/js/services.js b/hopsworks-cluster/src/main/webapp/js/services.js deleted file mode 100644 index d1cadedb68..0000000000 --- a/hopsworks-cluster/src/main/webapp/js/services.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -'use strict'; - -var services = angular.module("services", []); - -services.factory("ClusterService", ['$http', '$httpParamSerializerJQLike', - function ($http, $httpParamSerializerJQLike) { - var baseURL = getApiLocationBase(); - var service = { - register: function (cluster) { - var regReq = { - method: 'POST', - url: baseURL + '/cluster/register', - headers: { - 'Content-Type': 'application/json' - }, - data: cluster - }; - return $http(regReq); - }, - registerCluster: function (cluster) { - var regReq = { - method: 'POST', - url: baseURL + '/cluster/register/existing', - headers: { - 'Content-Type': 'application/json' - }, - data: cluster - }; - return $http(regReq); - }, - confirmRegister: function (validationKey) { - var regReq = { - method: 'GET', - url: baseURL + '/cluster/register/confirm/' + validationKey - }; - return $http(regReq); - }, - unregister: function (cluster) { - var regReq = { - method: 'POST', - url: baseURL + '/cluster/unregister', - headers: { - 'Content-Type': 'application/json' - }, - data: cluster - }; - return $http(regReq); - }, - confirmUnregister: function (validationKey) { - var regReq = { - method: 'GET', - url: baseURL + '/cluster/unregister/confirm/' + validationKey - }; - return $http(regReq); - }, - getAllClusters: function (cluster) { - var regReq = { - method: 'POST', - url: baseURL + '/cluster/all', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' - }, - data: $httpParamSerializerJQLike ({ - email: cluster.email, - pwd: cluster.chosenPassword - }) - }; - return $http(regReq); - }, - getCluster: function (cluster) { - var regReq = { - method: 'POST', - url: baseURL + '/cluster', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' - }, - data: $httpParamSerializerJQLike ({ - email: cluster.email, - pwd: cluster.chosenPassword, - orgName: cluster.organizationName, - orgUnitName: cluster.organizationalUnitName - }) - }; - return $http(regReq); - } - }; - return service; - }]); \ No newline at end of file diff --git a/hopsworks-cluster/src/main/webapp/js/util.js b/hopsworks-cluster/src/main/webapp/js/util.js deleted file mode 100644 index 3e3c00f186..0000000000 --- a/hopsworks-cluster/src/main/webapp/js/util.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -function getPort() { - var port = Number(location.port); - if (port === 'undefined' || port === 0) { - port = 80; - if (location.protocol === 'https:') { - port = 443; - } - } - - if (port === 3333 || port === 9000) { - port = 8080; - } - return port; -}; - -function getPathname() { - return "/hopsworks-cluster"; -} - -function getApiPath() { - return getPathname() + "/api"; -} - -function getApiLocationBase() { - return location.protocol + "//" + location.hostname +":" + getPort() + getApiPath(); -}; - - - - - diff --git a/hopsworks-cluster/src/main/webapp/package.json b/hopsworks-cluster/src/main/webapp/package.json deleted file mode 100644 index 8868b85e8e..0000000000 --- a/hopsworks-cluster/src/main/webapp/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "cluster-registration", - "version": "1.0.0", - "description": "hopsworks cluster registration", - "main": "index.js", - "devDependencies": { - "bower": "^1.3.1", - "karma": "~0.12", - "karma-chrome-launcher": "^0.1.4", - "karma-firefox-launcher": "^0.1.3", - "karma-jasmine": "^0.1.5", - "karma-junit-reporter": "^0.2.2", - "protractor": "~0.20.1", - "shelljs": "^0.2.6" - }, - "scripts": { - "postinstall": "bower install", - "prestart": "npm install", - "clean": "rm -rf node_modules && rm -rf vendor", - "pretest": "npm install", - "test": "karma start src/test/javascript/karma.conf.js", - "test-single-run": "karma start src/test/javascript/karma.conf.js --single-run", - "preupdate-webdriver": "npm install", - "update-webdriver": "webdriver-manager update", - "preprotractor": "npm run update-webdriver", - "update-index-async": "node -e \"require('shelljs/global'); sed('-i', /\\/\\/@@NG_LOADER_START@@[\\s\\S]*\\/\\/@@NG_LOADER_END@@/, '//@@NG_LOADER_START@@\\n' + cat('src/main/webapp/vendor/angular-loader/angular-loader.min.js') + '\\n//@@NG_LOADER_END@@', 'src/main/webapp/index.html');\"" - }, - "repository": { - "type": "git", - "url": "https://github.com/hopshadoop/hopsworks.git" - }, - "author": "ErmiasG", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/hopshadoop/hopsworks/issues" - }, - "homepage": "https://github.com/hopshadoop/hopsworks#readme" -} diff --git a/hopsworks-cluster/src/main/webapp/partials/home.html b/hopsworks-cluster/src/main/webapp/partials/home.html deleted file mode 100644 index fab1e7c9da..0000000000 --- a/hopsworks-cluster/src/main/webapp/partials/home.html +++ /dev/null @@ -1,55 +0,0 @@ - - - \ No newline at end of file diff --git a/hopsworks-cluster/src/main/webapp/partials/register.html b/hopsworks-cluster/src/main/webapp/partials/register.html deleted file mode 100644 index 7dd4a9c5f8..0000000000 --- a/hopsworks-cluster/src/main/webapp/partials/register.html +++ /dev/null @@ -1,320 +0,0 @@ - - -

{{title}}

-
-
-
- -
-
-
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - Passwords don't match. - -
-
- -
You must agree to the terms -
-
-
-
- -
-
-
-
- - Registering your new cluster... -
-
- -
-
-
- - - - -
-
-
- - -
- Back to home -
-
- -
-
-
-
-
- - - diff --git a/hopsworks-cluster/src/main/webapp/partials/registerCluster.html b/hopsworks-cluster/src/main/webapp/partials/registerCluster.html deleted file mode 100644 index 69976ed43b..0000000000 --- a/hopsworks-cluster/src/main/webapp/partials/registerCluster.html +++ /dev/null @@ -1,305 +0,0 @@ - - -

{{title}}

-
-
-
- -
-
-
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- -
You must agree to the terms -
-
-
-
- -
-
-
-
- - Registering your new cluster... -
-
- -
-
-
- - - - -
-
-
- - -
- Back to home -
-
- -
-
-
-
-
- - - - diff --git a/hopsworks-cluster/src/main/webapp/partials/unregister.html b/hopsworks-cluster/src/main/webapp/partials/unregister.html deleted file mode 100644 index f34c3c8ff7..0000000000 --- a/hopsworks-cluster/src/main/webapp/partials/unregister.html +++ /dev/null @@ -1,119 +0,0 @@ - - -

{{title}}

-
-
-
-
-
-
-
- - - -
-
- - - -
-
- - - -
-
- - - -
- -
-
- -
-
-
- - - - -
-
-
-
- - Unregistering your cluster... -
- - -

- Back to home -
-
-
-
-
\ No newline at end of file diff --git a/hopsworks-cluster/src/main/webapp/partials/viewRegisteredClusters.html b/hopsworks-cluster/src/main/webapp/partials/viewRegisteredClusters.html deleted file mode 100644 index 30c49b77db..0000000000 --- a/hopsworks-cluster/src/main/webapp/partials/viewRegisteredClusters.html +++ /dev/null @@ -1,128 +0,0 @@ - - -
-
-
-
-
-
-
- - - -
-
- - - -
-
-
- -
-
-
-
- -
- Back to home -
-
-
-
-
-
-
- -

Getting registered clusters ...

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-cluster.yml
-
-  hopsworks:
-    ...
-    hopssite:
-      version: "hops"
-  hopssite:
-    user: {{cluster.email}}
-    password: "change me"
-    cert:
-      o: {{cluster.organizationName}}
-      ou: {{cluster.organizationalUnitName}}
-    ...
-              
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/hopsworks-cluster/src/test/javascript/karma.conf.js b/hopsworks-cluster/src/test/javascript/karma.conf.js deleted file mode 100644 index cba57b9b8d..0000000000 --- a/hopsworks-cluster/src/test/javascript/karma.conf.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -module.exports = function(config){ - config.set({ - - basePath : '../../../', - - files : [ - 'src/main/webapp/vendor/angular**/**.min.js', - 'src/main/webapp/vendor/angular-mocks/angular-mocks.js', - 'src/main/webapp/js/**/*.js', - 'src/test/javascript/unit/**/*.js' - ], - - autoWatch : true, - - frameworks: ['jasmine'], - - browsers : ['Chrome'], - - plugins : [ - 'karma-chrome-launcher', - 'karma-firefox-launcher', - 'karma-jasmine', - 'karma-junit-reporter' - ], - - junitReporter : { - outputFile: 'target/test_out/unit.xml', - suite: 'src/test/javascript/unit' - } - - }); -}; diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dela/AddressJSON.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dela/AddressJSON.java deleted file mode 100644 index 456e42dd91..0000000000 --- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dela/AddressJSON.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.common.dela; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class AddressJSON { - - private String ip; - private int port; - private String id; - private String nat; - - public AddressJSON() { - - } - - public AddressJSON(String ip, int port, String id, String nat) { - this.ip = ip; - this.port = port; - this.id = id; - this.nat = nat; - } - - public String getIp() { - return ip; - } - - public int getPort() { - return port; - } - - public String getId() { - return id; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public void setPort(int port) { - this.port = port; - } - - public void setId(String id) { - this.id = id; - } - - public String getNat() { - return nat; - } - - public void setNat(String nat) { - this.nat = nat; - } - - @Override - public String toString() { - return "AddressJSON{" + "ip=" + ip + ", port=" + port + ", id=" + id + ", nat=" + nat + '}'; - } -} diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dela/DelaClientType.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/dela/DelaClientType.java deleted file mode 100644 index e0751b7a8b..0000000000 --- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/dela/DelaClientType.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.common.dela; - -/** - * used in delaClientCtrl.js - make sure to sync - */ -public enum DelaClientType { - - BASE_CLIENT("BASE_CLIENT"), - FULL_CLIENT("FULL_CLIENT"); - - public final String type; - - DelaClientType(String type) { - this.type = type; - } - - public static DelaClientType from(String clientType) { - switch (clientType) { - case "BASE_CLIENT": - return BASE_CLIENT; - case "FULL_CLIENT": - return FULL_CLIENT; - default: - return BASE_CLIENT; - } - } -} diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/proxies/CAProxy.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/proxies/CAProxy.java index 5467fedc38..ca071fd65a 100644 --- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/proxies/CAProxy.java +++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/proxies/CAProxy.java @@ -70,7 +70,6 @@ public class CAProxy { private enum CA_PATH { PROJECT_CA_PATH(CA_BASE_PATH + "project"), - DELA_CA_PATH(CA_BASE_PATH + "dela"), HOST_CA_PATH(CA_BASE_PATH + "host"); private final String path; @@ -99,11 +98,7 @@ public void init() { public CSR signProjectCSR(CSR csr) throws HopsSecurityException, GenericException { return signCSR(csr, CA_PATH.PROJECT_CA_PATH); } - - public CSR signDelaCSR(CSR csr) throws HopsSecurityException, GenericException { - return signCSR(csr, CA_PATH.DELA_CA_PATH); - } - + private CSR signCSR(CSR csr, CA_PATH path) throws HopsSecurityException, GenericException{ try { String csrJSON = objectMapper.writeValueAsString(csr); @@ -134,10 +129,6 @@ public CSR performAction() throws ClientProtocolException, IOException { public void revokeProjectX509(String certificateIdentifier) throws HopsSecurityException, GenericException { revokeX509(CERTIFICATE_IDENTIFIER, certificateIdentifier, CA_PATH.PROJECT_CA_PATH.path); } - - public void revokeDelaX509(String certificateIdentifier) throws HopsSecurityException, GenericException { - revokeX509(CERTIFICATE_IDENTIFIER, certificateIdentifier, CA_PATH.DELA_CA_PATH.path); - } public void revokeHostX509(String hostname) throws HopsSecurityException, GenericException { revokeX509(HOSTNAME, hostname, CA_PATH.HOST_CA_PATH.path + "/all"); diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/CertificatesController.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/CertificatesController.java index 1b3063eb49..7512b35c6a 100644 --- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/CertificatesController.java +++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/CertificatesController.java @@ -127,8 +127,7 @@ public class CertificatesController { private CertificateFactory certificateFactory = null; private enum Endpoint { - PROJECT("project"), - DELA("dela"); + PROJECT("project"); private final String endpointPath; @@ -231,16 +230,6 @@ public void revokeUserSpecificCertificates(Project project, Users user) } } - public CSR signDelaClusterCertificate(CSR csr) - throws GenericException, HopsSecurityException, UnsupportedEncodingException { - return signCSR(csr, Endpoint.DELA); - } - - public void revokeDelaClusterCertificate(String certificateIdentifier) - throws GenericException, HopsSecurityException { - revokeCertificate(certificateIdentifier, Endpoint.DELA); - } - public BigInteger extractSerialNumberFromCert(String certificate) throws CertificateException { InputStream certStream = new ByteArrayInputStream(certificate.getBytes()); CertificateFactory cf = CertificateFactory.getInstance("X.509"); @@ -322,8 +311,6 @@ private CSR signCSR(CSR csr, Endpoint endpoint) throws HopsSecurityException, Ge switch (endpoint) { case PROJECT: return caProxy.signProjectCSR(csr); - case DELA: - return caProxy.signDelaCSR(csr); default: throw new HopsSecurityException(RESTCodes.SecurityErrorCode.CSR_ERROR, Level.FINE, null, "Unknown CSR type " + endpoint.toString()); @@ -336,9 +323,6 @@ private void revokeCertificate(String certificateIdentifier, Endpoint endpoint) case PROJECT: caProxy.revokeProjectX509(certificateIdentifier); break; - case DELA: - caProxy.revokeDelaX509(certificateIdentifier); - break; default: throw new HopsSecurityException(RESTCodes.SecurityErrorCode.CERTIFICATE_REVOKATION_ERROR, Level.FINE, null, "Unknown revocation type " + endpoint.toString()); diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/DelaCertsMasterPasswordHandler.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/DelaCertsMasterPasswordHandler.java deleted file mode 100644 index fab2f1c7e2..0000000000 --- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/security/DelaCertsMasterPasswordHandler.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.common.security; - -import io.hops.hopsworks.persistence.entity.dela.certs.ClusterCertificate; -import io.hops.hopsworks.common.dao.dela.certs.ClusterCertificateFacade; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.exceptions.EncryptionMasterPasswordException; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Stateless -@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) -public class DelaCertsMasterPasswordHandler implements MasterPasswordHandler { - private final Logger LOGGER = Logger.getLogger(DelaCertsMasterPasswordHandler.class.getName()); - - @EJB - private ClusterCertificateFacade clusterCertificateFacade; - @EJB - private Settings settings; - - @Override - public void pre() { - - } - - @Override - public MasterPasswordChangeResult perform(String oldMasterPassword, String newMasterPassword) { - StringBuilder successLog = new StringBuilder(); - successLog.append("Performing change of master password for Dela certificates\n"); - Map items2rollback = new HashMap<>(); - - Optional> maybe = clusterCertificateFacade.getAllClusterCerts(); - if (maybe.isPresent()) { - LOGGER.log(Level.INFO, "Updating Dela certs with new Hopsworks master encryption password"); - String mapKey = null, oldPassword, newEncCertPassword; - try { - for (ClusterCertificate cert : maybe.get()) { - mapKey = cert.getClusterName(); - oldPassword = cert.getCertificatePassword(); - items2rollback.putIfAbsent(mapKey, oldPassword); - newEncCertPassword = getNewUserPassword(settings.getHopsSiteClusterPswd().get(), oldPassword, - oldMasterPassword, newMasterPassword); - cert.setCertificatePassword(newEncCertPassword); - clusterCertificateFacade.updateClusterCerts(cert); - successLog.append("Updated certificate: ").append(mapKey).append("\n"); - } - } catch (Exception ex) { - String errorMsg = "Something went wrong while updating master encryption password for Cluster Certificates. " + - "Cluster certificate provoked the error was: " + mapKey; - LOGGER.log(Level.SEVERE, errorMsg + " rolling back...", ex); - return new MasterPasswordChangeResult<>(items2rollback, - new EncryptionMasterPasswordException(errorMsg)); - } - } - return new MasterPasswordChangeResult<>(successLog, items2rollback, null); - } - - @Override - @SuppressWarnings("unchecked") - public void rollback(MasterPasswordChangeResult result) { - LOGGER.log(Level.INFO, "Rolling back Dela certificates"); - Map items2rollback = (HashMap) result.getRollbackItems(); - for (Map.Entry cert : items2rollback.entrySet()) { - String key = cert.getKey(); - String value = cert.getValue(); - Optional optional = clusterCertificateFacade.getClusterCert(key); - if (optional.isPresent()) { - ClusterCertificate cc = optional.get(); - cc.setCertificatePassword(value); - clusterCertificateFacade.updateClusterCerts(cc); - } - } - } - - @Override - public void post() { - - } -} diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/LocalhostServices.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/LocalhostServices.java deleted file mode 100644 index 3db09f1529..0000000000 --- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/LocalhostServices.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.common.util; - -import java.io.File; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class LocalhostServices { - - private static final Logger logger = Logger.getLogger(LocalhostServices.class.getName()); - - //Dela Certificates - public static void generateHopsSiteKeystore(Settings settings, OSProcessExecutor osProcessExecutor, - String userKeyPwd) throws IOException { - - ProcessDescriptor processDescriptor = new ProcessDescriptor.Builder() - .addCommand("/usr/bin/sudo") - .addCommand(settings.getHopsSiteCaScript()) - .addCommand(userKeyPwd) - .build(); - - ProcessResult processResult = osProcessExecutor.execute(processDescriptor); - if (!processResult.processExited()) { - throw new IOException("Generating Hops site keystore time-out"); - } - int exitCode = processResult.getExitCode(); - if (exitCode != 0) { - throw new IOException("stdout: " + processResult.getStdout() + "\nstderr: " + processResult.getStderr()); - } - } - //Dela Certificates end - - /** - * We should NOT pass EJBs as method arguments!!! - * - * @param base - root directory for calculating disk usage for the subtree - * @return the disk usage in Bytes for the subtree rooted at base. - */ - public static String du(OSProcessExecutor osProcessExecutor, File base) throws IOException { - -// This java implementation is like 100 times slower than calling 'du -sh' -// long totalBytes = base.length(); -// if (base.isDirectory()) { -// for (String child : base.list()) { -// File inode = new File(base, child); -// totalBytes += du(inode); -// } -// } -// return totalBytes; - - ProcessDescriptor processDescriptor = new ProcessDescriptor.Builder() - .addCommand("du") - .addCommand("-sh") - .addCommand(base.getCanonicalPath()) - .build(); - - logger.log(Level.FINE, processDescriptor.toString()); - try { - ProcessResult processResult = osProcessExecutor.execute(processDescriptor); - if (!processResult.processExited()) { - logger.log(Level.SEVERE, "Operation to calculate disk usage time-out"); - return ""; - } - return processResult.getStdout(); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Problem getting logs: {0}", ex. - toString()); - } - return ""; - } - -} diff --git a/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/Settings.java b/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/Settings.java index 08694bae47..59404988b5 100644 --- a/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/Settings.java +++ b/hopsworks-common/src/main/java/io/hops/hopsworks/common/util/Settings.java @@ -42,8 +42,6 @@ import com.google.common.base.Strings; import io.hops.hopsworks.common.dao.user.UserFacade; import io.hops.hopsworks.common.dataset.util.CompressionInfo; -import io.hops.hopsworks.common.dela.AddressJSON; -import io.hops.hopsworks.common.dela.DelaClientType; import io.hops.hopsworks.common.hdfs.DistributedFileSystemOps; import io.hops.hopsworks.common.provenance.core.Provenance; import io.hops.hopsworks.common.provenance.core.dto.ProvTypeDTO; @@ -220,7 +218,6 @@ public class Settings implements Serializable { private static final String VARIABLE_SPARK_VERSION = "spark_version"; private static final String VARIABLE_FLINK_VERSION = "flink_version"; private static final String VARIABLE_EPIPE_VERSION = "epipe_version"; - private static final String VARIABLE_DELA_VERSION = "dela_version"; private static final String VARIABLE_KAFKA_VERSION = "kafka_version"; private static final String VARIABLE_OPENSEARCH_VERSION = "elastic_version"; private static final String VARIABLE_TENSORFLOW_VERSION = "tensorflow_version"; @@ -669,7 +666,6 @@ private void populateCache() { jupyterShutdownTimerInterval = setStrVar(JUPYTER_SHUTDOWN_TIMER_INTERVAL, jupyterShutdownTimerInterval); checkNodemanagersStatus = setBoolVar(VARIABLE_CHECK_NODEMANAGERS_STATUS, checkNodemanagersStatus); - populateDelaCache(); populateLDAPCache(); ZOOKEEPER_VERSION = setStrVar(VARIABLE_ZOOKEEPER_VERSION, ZOOKEEPER_VERSION); @@ -684,7 +680,6 @@ private void populateCache() { SPARK_VERSION = setStrVar(VARIABLE_SPARK_VERSION, SPARK_VERSION); FLINK_VERSION = setStrVar(VARIABLE_FLINK_VERSION, FLINK_VERSION); EPIPE_VERSION = setStrVar(VARIABLE_EPIPE_VERSION, EPIPE_VERSION); - DELA_VERSION = setStrVar(VARIABLE_DELA_VERSION, DELA_VERSION); KAFKA_VERSION = setStrVar(VARIABLE_KAFKA_VERSION, KAFKA_VERSION); OPENSEARCH_VERSION = setStrVar(VARIABLE_OPENSEARCH_VERSION, OPENSEARCH_VERSION); TENSORFLOW_VERSION = setStrVar(VARIABLE_TENSORFLOW_VERSION, TENSORFLOW_VERSION); @@ -2483,66 +2478,18 @@ public synchronized String getEmailVerificationEndpoint() { } //Dela START - private static final String VARIABLE_HOPSSITE_BASE_URI = "hops_site_endpoint"; - private static final String VARIABLE_HOPSSITE_BASE_URI_HOST = "hops_site_host"; private static final String VARIABLE_CLUSTER_CERT = "hopsworks_certificate"; - private static final String VARIABLE_DELA_ENABLED = "dela_enabled"; - private static final String VARIABLE_DELA_CLIENT_TYPE = "dela_client_type"; - private static final String VARIABLE_HOPSSITE_HEARTBEAT_INTERVAL = "hopssite_heartbeat_interval"; - - private static final String VARIABLE_DELA_CLUSTER_ID = "cluster_id"; - private static final String VARIABLE_DELA_CLUSTER_IP = "dela_cluster_ip"; - private static final String VARIABLE_DELA_CLUSTER_HTTP_PORT = "dela_cluster_http_port"; - private static final String VARIABLE_DELA_PUBLIC_HOPSWORKS_PORT = "dela_hopsworks_public_port"; - private static final String VARIABLE_PUBLIC_HTTPS_PORT = "public_https_port"; - private static final String VARIABLE_DELA_SEARCH_ENDPOINT = "dela_search_endpoint"; - private static final String VARIABLE_DELA_TRANSFER_ENDPOINT = "dela_transfer_endpoint"; private static final String VARIABLE_HOPSWORKS_PUBLIC_HOST = "hopsworks_public_host"; - public static final Level DELA_DEBUG = Level.INFO; - private String HOPSSITE_HOST = "hops.site"; - private String HOPSSITE = "http://hops.site:5081/hops-site/api"; private Boolean DELA_ENABLED = false; // set to false if not found in variables table - private DelaClientType DELA_CLIENT_TYPE = DelaClientType.FULL_CLIENT; - - private long HOPSSITE_HEARTBEAT_RETRY = 10 * 1000l; //10s - private long HOPSSITE_HEARTBEAT_INTERVAL = 10 * 60 * 1000l;//10min - - private String DELA_TRANSFER_IP = "localhost"; - private String DELA_TRANSFER_HTTP_PORT = "42000"; - private String DELA_PUBLIC_HOPSWORK_PORT = "8080"; - private String PUBLIC_HTTPS_PORT = "8181"; - - //set on registration after Dela is contacted to detect public port - private String DELA_SEARCH_ENDPOINT = ""; - private String DELA_TRANSFER_ENDPOINT = ""; - //set on cluster registration - private String DELA_CLUSTER_ID = null; - // - private AddressJSON DELA_PUBLIC_ENDPOINT = null; - // - public static final String MANIFEST_FILE = "manifest.json"; - public static final String README_FILE = "README.md"; - private void populateDelaCache() { - DELA_ENABLED = setBoolVar(VARIABLE_DELA_ENABLED, DELA_ENABLED); - DELA_CLIENT_TYPE = DelaClientType.from(setVar(VARIABLE_DELA_CLIENT_TYPE, DELA_CLIENT_TYPE.type)); - HOPSSITE_CLUSTER_NAME = setVar(VARIABLE_HOPSSITE_CLUSTER_NAME, HOPSSITE_CLUSTER_NAME); - HOPSSITE_CLUSTER_PSWD = setVar(VARIABLE_HOPSSITE_CLUSTER_PSWD, HOPSSITE_CLUSTER_PSWD); - HOPSSITE_CLUSTER_PSWD_AUX = setVar(VARIABLE_HOPSSITE_CLUSTER_PSWD_AUX, HOPSSITE_CLUSTER_PSWD_AUX); - HOPSSITE_HOST = setVar(VARIABLE_HOPSSITE_BASE_URI_HOST, HOPSSITE_HOST); - HOPSSITE = setVar(VARIABLE_HOPSSITE_BASE_URI, HOPSSITE); - HOPSSITE_HEARTBEAT_INTERVAL = setLongVar(VARIABLE_HOPSSITE_HEARTBEAT_INTERVAL, HOPSSITE_HEARTBEAT_INTERVAL); - - DELA_TRANSFER_IP = setStrVar(VARIABLE_DELA_CLUSTER_IP, DELA_TRANSFER_IP); - DELA_TRANSFER_HTTP_PORT = setStrVar(VARIABLE_DELA_CLUSTER_HTTP_PORT, DELA_TRANSFER_HTTP_PORT); - DELA_SEARCH_ENDPOINT = setStrVar(VARIABLE_DELA_SEARCH_ENDPOINT, DELA_SEARCH_ENDPOINT); - DELA_TRANSFER_ENDPOINT = setStrVar(VARIABLE_DELA_TRANSFER_ENDPOINT, DELA_TRANSFER_ENDPOINT); - DELA_PUBLIC_HOPSWORK_PORT = setStrVar(VARIABLE_DELA_PUBLIC_HOPSWORKS_PORT, DELA_PUBLIC_HOPSWORK_PORT); - PUBLIC_HTTPS_PORT = setStrVar(VARIABLE_PUBLIC_HTTPS_PORT, PUBLIC_HTTPS_PORT); - DELA_CLUSTER_ID = setStrVar(VARIABLE_DELA_CLUSTER_ID, DELA_CLUSTER_ID); + public static final String README_FILE = "README.md"; + + public synchronized Boolean isDelaEnabled() { + checkCache(); + return DELA_ENABLED; } - + private void populateServiceJWTCache() { SERVICE_MASTER_JWT = setStrVar(VARIABLE_SERVICE_MASTER_JWT, SERVICE_MASTER_JWT); RENEW_TOKENS = new String[NUM_OF_SERVICE_RENEW_TOKENS]; @@ -2552,215 +2499,7 @@ private void populateServiceJWTCache() { RENEW_TOKENS[i] = token; } } - - public synchronized Boolean isDelaEnabled() { - checkCache(); - return DELA_ENABLED; - } - - public synchronized DelaClientType getDelaClientType() { - return DELA_CLIENT_TYPE; - } - - public synchronized String getHOPSSITE_HOST() { - checkCache(); - return HOPSSITE_HOST; - } - - public synchronized String getHOPSSITE() { - checkCache(); - return HOPSSITE; - } - - public synchronized long getHOPSSITE_HEARTBEAT_RETRY() { - checkCache(); - return HOPSSITE_HEARTBEAT_RETRY; - } - - public synchronized long getHOPSSITE_HEARTBEAT_INTERVAL() { - checkCache(); - return HOPSSITE_HEARTBEAT_INTERVAL; - } - - public synchronized String getDELA_TRANSFER_IP() { - checkCache(); - return DELA_TRANSFER_IP; - } - - public synchronized String getDELA_TRANSFER_HTTP_PORT() { - checkCache(); - return DELA_TRANSFER_HTTP_PORT; - } - - public synchronized String getDELA_TRANSFER_HTTP_ENDPOINT() { - checkCache(); - return "http://" + DELA_TRANSFER_IP + ":" + DELA_TRANSFER_HTTP_PORT + "/"; - } - - public synchronized String getDELA_HOPSWORKS_PORT() { - checkCache(); - return DELA_PUBLIC_HOPSWORK_PORT; - } - - public synchronized String getPUBLIC_HTTPS_PORT() { - checkCache(); - return PUBLIC_HTTPS_PORT; - } - - public synchronized AddressJSON getDELA_PUBLIC_ENDPOINT() { - return DELA_PUBLIC_ENDPOINT; - } - - public synchronized String getDELA_SEARCH_ENDPOINT() { - checkCache(); - if (DELA_SEARCH_ENDPOINT != null) { - return DELA_SEARCH_ENDPOINT; - } - return setStrVar(DELA_SEARCH_ENDPOINT, null); - } - - public synchronized String getDELA_TRANSFER_ENDPOINT() { - checkCache(); - if (DELA_TRANSFER_ENDPOINT != null) { - return DELA_TRANSFER_ENDPOINT; - } - return setStrVar(DELA_TRANSFER_ENDPOINT, null); - } - - public synchronized void setDELA_PUBLIC_ENDPOINT(AddressJSON endpoint) { - DELA_PUBLIC_ENDPOINT = endpoint; - - String delaSearchEndpoint = "https://" + endpoint.getIp() + ":" - + getPUBLIC_HTTPS_PORT() + "/hopsworks-api/api"; - String delaTransferEndpoint = endpoint.getIp() + ":" + endpoint.getPort() + "/" + endpoint.getId(); - - if (getDELA_SEARCH_ENDPOINT() == null) { - em.persist(new Variables(VARIABLE_DELA_SEARCH_ENDPOINT, delaSearchEndpoint)); - } else { - em.merge(new Variables(VARIABLE_DELA_SEARCH_ENDPOINT, delaSearchEndpoint)); - } - DELA_SEARCH_ENDPOINT = delaSearchEndpoint; - - if (getDELA_TRANSFER_ENDPOINT() == null) { - em.persist(new Variables(VARIABLE_DELA_TRANSFER_ENDPOINT, delaTransferEndpoint)); - } else { - em.merge(new Variables(VARIABLE_DELA_TRANSFER_ENDPOINT, delaTransferEndpoint)); - } - DELA_TRANSFER_ENDPOINT = delaTransferEndpoint; - } - - public synchronized void setDELA_CLUSTER_ID(String id) { - if (getDELA_CLUSTER_ID() == null) { - em.persist(new Variables(VARIABLE_DELA_CLUSTER_ID, id)); - } else { - em.merge(new Variables(VARIABLE_DELA_CLUSTER_ID, id)); - } - DELA_CLUSTER_ID = id; - } - - public synchronized String getDELA_CLUSTER_ID() { - checkCache(); - if (DELA_CLUSTER_ID != null) { - return DELA_CLUSTER_ID; - } - return setStrVar(VARIABLE_DELA_CLUSTER_ID, null); - } - - public synchronized String getDELA_DOMAIN() { - if (DELA_PUBLIC_ENDPOINT != null) { - return DELA_PUBLIC_ENDPOINT.getIp(); - } - return null; - } - - //************************************************CERTIFICATES******************************************************** - private static final String HOPS_SITE_CA_DIR = "hops-site-certs"; - private final static String HOPS_SITE_CERTFILE = "/pub.pem"; - private final static String HOPS_SITE_CA_CERTFILE = "/ca_pub.pem"; - private final static String HOPS_SITE_INTERMEDIATE_CERTFILE = "/intermediate_ca_pub.pem"; - private final static String HOPS_SITE_KEY_STORE = "/keystores/keystore.jks"; - private final static String HOPS_SITE_TRUST_STORE = "/keystores/truststore.jks"; - - private static final String VARIABLE_HOPSSITE_CLUSTER_NAME = "hops_site_cluster_name"; - private static final String VARIABLE_HOPSSITE_CLUSTER_PSWD = "hops_site_cluster_pswd"; - private static final String VARIABLE_HOPSSITE_CLUSTER_PSWD_AUX = "hops_site_cluster_pswd_aux"; - - private String HOPSSITE_CLUSTER_NAME = null; - private String HOPSSITE_CLUSTER_PSWD = null; - private String HOPSSITE_CLUSTER_PSWD_AUX = "1234"; - - public synchronized Optional getHopsSiteClusterName() { - checkCache(); - return Optional.ofNullable(HOPSSITE_CLUSTER_NAME); - } - - public synchronized void setHopsSiteClusterName(String clusterName) { - if (getHopsSiteClusterName().isPresent()) { - em.merge(new Variables(VARIABLE_HOPSSITE_CLUSTER_NAME, clusterName)); - } else { - em.persist(new Variables(VARIABLE_HOPSSITE_CLUSTER_NAME, clusterName)); - } - HOPSSITE_CLUSTER_NAME = clusterName; - } - - public synchronized void deleteHopsSiteClusterName() { - if (getHopsSiteClusterName().isPresent()) { - Optional v = findById(VARIABLE_HOPSSITE_CLUSTER_NAME); - if (v.isPresent()) { - em.remove(v); - HOPSSITE_CLUSTER_NAME = null; - } - } - } - - public synchronized String getHopsSiteClusterPswdAux() { - checkCache(); - return HOPSSITE_CLUSTER_PSWD_AUX; - } - - public synchronized Optional getHopsSiteClusterPswd() { - checkCache(); - return Optional.ofNullable(HOPSSITE_CLUSTER_PSWD); - } - - public synchronized void setHopsSiteClusterPswd(String pswd) { - if (getHopsSiteClusterPswd().isPresent()) { - em.merge(new Variables(VARIABLE_HOPSSITE_CLUSTER_PSWD, pswd)); - } else { - em.persist(new Variables(VARIABLE_HOPSSITE_CLUSTER_PSWD, pswd)); - } - HOPSSITE_CLUSTER_PSWD = pswd; - } - - public synchronized String getHopsSiteCaDir() { - return getCertsDir() + File.separator + HOPS_SITE_CA_DIR; - } - - public synchronized String getHopsSiteCaScript() { - return getSudoersDir() + File.separator + "ca-keystore.sh"; - } - - public synchronized String getHopsSiteCert() { - return getHopsSiteCaDir() + HOPS_SITE_CERTFILE; - } - - public synchronized String getHopsSiteCaCert() { - return getHopsSiteCaDir() + HOPS_SITE_CA_CERTFILE; - } - - public synchronized String getHopsSiteIntermediateCert() { - return getHopsSiteCaDir() + HOPS_SITE_INTERMEDIATE_CERTFILE; - } - - public synchronized String getHopsSiteKeyStorePath() { - return getHopsSiteCaDir() + HOPS_SITE_KEY_STORE; - } - - public synchronized String getHopsSiteTrustStorePath() { - return getHopsSiteCaDir() + HOPS_SITE_TRUST_STORE; - } - //Dela END - + //************************************************ZOOKEEPER******************************************************** public static final int ZOOKEEPER_SESSION_TIMEOUT_MS = 30 * 1000;//30 seconds public static final int ZOOKEEPER_CONNECTION_TIMEOUT_MS = 30 * 1000;// 30 seconds @@ -3288,13 +3027,6 @@ public synchronized String getKafkaVersion() { return KAFKA_VERSION; } - private String DELA_VERSION; - - public synchronized String getDelaVersion() { - checkCache(); - return DELA_VERSION; - } - private String EPIPE_VERSION; public synchronized String getEpipeVersion() { diff --git a/hopsworks-dela/pom.xml b/hopsworks-dela/pom.xml deleted file mode 100644 index ab54d9407d..0000000000 --- a/hopsworks-dela/pom.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - 4.0.0 - - hopsworks - io.hops - 3.1.0-SNAPSHOT - - - io.hops.hopsworks - hopsworks-dela - 3.1.0-SNAPSHOT - ejb - - hopsworks-dela - - - ${project.build.directory}/endorsed - UTF-8 - 1.2 - true - - - - - io.hops.hopsworks - hopsworks-rest-utils - - - - javax - javaee-api - - - - io.hops.hopsworks - hopsworks-common - - - io.hops - hadoop-client-api - - - ejb - provided - - - - - hopsworks-dela - - - org.apache.maven.plugins - maven-ejb-plugin - - - - - diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaDatasetController.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaDatasetController.java deleted file mode 100644 index c504ce2a41..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaDatasetController.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela; - -import io.hops.hopsworks.common.dao.dataset.DatasetFacade; -import io.hops.hopsworks.common.dao.dataset.DatasetSharedWithFacade; -import io.hops.hopsworks.common.dataset.DatasetController; -import io.hops.hopsworks.common.dataset.FilePreviewDTO; -import io.hops.hopsworks.common.hdfs.DistributedFileSystemOps; -import io.hops.hopsworks.common.hdfs.DistributedFsService; -import io.hops.hopsworks.common.provenance.core.HopsFSProvenanceController; -import io.hops.hopsworks.common.provenance.core.dto.ProvTypeDTO; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.exceptions.DatasetException; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.exceptions.HopsSecurityException; -import io.hops.hopsworks.exceptions.ProvenanceException; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import io.hops.hopsworks.persistence.entity.dataset.DatasetAccessPermission; -import io.hops.hopsworks.persistence.entity.dataset.SharedState; -import io.hops.hopsworks.persistence.entity.log.operation.OperationType; -import io.hops.hopsworks.persistence.entity.project.Project; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.restutils.RESTCodes; -import org.apache.hadoop.fs.Path; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import java.io.IOException; -import java.util.List; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Stateless -@TransactionAttribute(TransactionAttributeType.NEVER) -public class DelaDatasetController { - - private static final Logger LOGGER = Logger.getLogger(DelaDatasetController.class.getName()); - - @EJB - private DatasetController datasetCtrl; - @EJB - private DatasetFacade datasetFacade; - @EJB - private DatasetController datasetController; - @EJB - private DatasetSharedWithFacade datasetSharedWithFacade; - @EJB - private DistributedFsService dfs; - @EJB - private HopsFSProvenanceController fsProvenanceController; - - public Dataset uploadToHops(Project project, Dataset dataset, String publicDSId) { - dataset.setPublicDsState(SharedState.HOPS); - dataset.setPublicDsId(publicDSId); - //TODO:Alex move to set hdfs permissions - //datasetController.setPermissions(); - //dataset.setEditable(DatasetPermissions.OWNER_ONLY); - datasetFacade.merge(dataset); - datasetCtrl.logDataset(project, dataset, OperationType.Update); - return dataset; - } - - public Dataset unshareFromHops(Project project, Dataset dataset) { - dataset.setPublicDsState(SharedState.PRIVATE); - dataset.setPublicDsId(null); - //datasetController.setPermissions(); - //dataset.setEditable(DatasetPermissions.GROUP_WRITABLE_SB); - datasetFacade.merge(dataset); - datasetCtrl.logDataset(project, dataset, OperationType.Update); - return dataset; - } - - public Dataset download(Project project, Users user, String publicDSId, String name) - throws DelaException, ProvenanceException { - Dataset dataset; - try { - dataset = createDataset(user, project, name, ""); - } catch (DatasetException | HopsSecurityException e) { - throw new DelaException(RESTCodes.DelaErrorCode.THIRD_PARTY_ERROR, Level.SEVERE, DelaException.Source.LOCAL, null, - e.getMessage(), e); - } - dataset.setPublicDsState(SharedState.HOPS); - dataset.setPublicDsId(publicDSId); - //datasetController.setPermissions(); - //dataset.setEditable(DatasetPermissions.OWNER_ONLY); - datasetFacade.merge(dataset); - datasetCtrl.logDataset(project, dataset, OperationType.Update); - return dataset; - } - - public Dataset updateDescription(Project project, Dataset dataset, String description) { - dataset.setDescription(description); - datasetFacade.merge(dataset); - datasetCtrl.logDataset(project, dataset, OperationType.Update); - return dataset; - } - - public void delete(Project project, Dataset dataset, Users user) throws DelaException, IOException, DatasetException { - if (dataset.isShared(project)) { - //remove the entry in the table that represents shared ds - //but leave the dataset in hdfs b/c the user does not have the right to delete it. - datasetController.unshareDataset(project, user, project, dataset); - return; - } - try { - Path path = datasetCtrl.getDatasetPath(dataset); - boolean result = datasetCtrl.deleteDatasetDir(dataset, path, dfs.getDfsOps()); - if (!result) { - throw new DelaException(RESTCodes.DelaErrorCode.DATASET_DELETE_ERROR, Level.SEVERE, DelaException.Source.LOCAL); - } - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.DATASET_DELETE_ERROR, Level.SEVERE, DelaException.Source.LOCAL, - null, ex.getMessage(), ex); - } - } - - public Dataset createDataset(Users user, Project project, String name, String description) - throws DatasetException, HopsSecurityException, ProvenanceException { - DistributedFileSystemOps dfso = dfs.getDfsOps(); - try { - ProvTypeDTO projectMetaStatus = fsProvenanceController.getProjectProvType(user, project); - datasetCtrl.createDataset(user, project, name, description, projectMetaStatus, - false, DatasetAccessPermission.EDITABLE, dfso); - return datasetController.getByProjectAndDsName(project, null, name); - } finally { - if(dfso != null) { - dfso.close(); - } - } - } - - public List getLocalPublicDatasets() { - return datasetFacade.findPublicDatasetsByState(SharedState.HOPS.state); - } - - public Optional isPublicDatasetLocal(String publicDsId) { - return datasetFacade.findByPublicDsId(publicDsId); - } - - public FilePreviewDTO getLocalReadmeForPublicDataset(Dataset dataset) throws IOException, IllegalAccessException { - if(!dataset.isPublicDs()) { - throw new IllegalAccessException("dataset is not public"); - } - Path datasetPath = datasetCtrl.getDatasetPath(dataset); - Path readmePath = new Path(datasetPath, Settings.README_FILE); - return datasetCtrl.getReadme(readmePath.toString(), dfs.getDfsOps()); - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaHdfsController.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaHdfsController.java deleted file mode 100644 index 50a39880ce..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaHdfsController.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.dela; - -import io.hops.hopsworks.common.dataset.DatasetController; -import io.hops.hopsworks.common.dataset.FilePreviewDTO; -import io.hops.hopsworks.common.hdfs.DistributedFileSystemOps; -import io.hops.hopsworks.common.hdfs.DistributedFsService; -import io.hops.hopsworks.common.hdfs.HdfsUsersController; -import io.hops.hopsworks.common.hdfs.inode.InodeController; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.old_dto.FileInfo; -import io.hops.hopsworks.dela.old_dto.ManifestJSON; -import io.hops.hopsworks.dela.util.ManifestHelper; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import io.hops.hopsworks.persistence.entity.hdfs.inode.Inode; -import io.hops.hopsworks.persistence.entity.project.Project; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.restutils.RESTCodes; -import org.apache.hadoop.fs.FSDataInputStream; -import org.apache.hadoop.fs.FSDataOutputStream; -import org.apache.hadoop.fs.Path; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import java.io.File; -import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Stateless -@TransactionAttribute(TransactionAttributeType.NEVER) -public class DelaHdfsController { - - private static final Logger LOGGER = Logger.getLogger(DelaHdfsController.class.getName()); - - @EJB - private DatasetController datasetCtrl; - @EJB - private InodeController inodeController; - @EJB - private HdfsUsersController hdfsUsersBean; - @EJB - private DistributedFsService dfs; - - public long datasetSize(Project project, Dataset dataset, Users user) throws DelaException { - return length(project, user, datasetCtrl.getDatasetPath(dataset)); - } - - private Path manifestPath(Dataset dataset) { - Path datasetPath = datasetCtrl.getDatasetPath(dataset); - Path manifestPath = new Path(datasetPath, Settings.MANIFEST_FILE); - return manifestPath; - } - - public ManifestJSON readManifest(Project project, Dataset dataset, Users user) throws DelaException { - byte[] manifestBytes = read(project, user, manifestPath(dataset)); - ManifestJSON manifest = ManifestHelper.unmarshall(manifestBytes); - return manifest; - } - - public ManifestJSON writeManifest(Project project, Dataset dataset, Users user) throws DelaException { - if (inodeController.getChildren(dataset.getInode()).isEmpty()) { - throw new DelaException(RESTCodes.DelaErrorCode.DATASET_EMPTY, Level.WARNING, DelaException.Source.LOCAL); - } - LOGGER.log(Settings.DELA_DEBUG, "{0} - writing manifest", dataset.getPublicDsId()); - ManifestJSON manifest = createManifest(project, dataset, user); - Path manifestPath = manifestPath(dataset); - delete(project, user, manifestPath); - write(project, user, manifestPath, ManifestHelper.marshall(manifest)); - return manifest; - } - - public void deleteManifest(Project project, Dataset dataset, Users user) throws DelaException { - delete(project, user, manifestPath(dataset)); - } - - private Path readmePath(Dataset dataset) { - Path datasetPath = datasetCtrl.getDatasetPath(dataset); - Path readmePath = new Path(datasetPath, Settings.README_FILE); - return readmePath; - } - - public String getReadme(Project project, Dataset dataset, Users user) throws DelaException { - LOGGER.log(Settings.DELA_DEBUG, "dela:hdfs:readme"); - String result = new String(read(project, user, readmePath(dataset))); - LOGGER.log(Settings.DELA_DEBUG, "dela:hdfs:readme:done"); - return result; - } - - public FilePreviewDTO getPublicReadme(Dataset dataset) throws DelaException { - LOGGER.log(Settings.DELA_DEBUG, "dela:hdfs:readme"); - DistributedFileSystemOps dfso = dfs.getDfsOps(); - FilePreviewDTO result = new FilePreviewDTO("text", "md", new String(read(dfso, readmePath(dataset)))); - LOGGER.log(Settings.DELA_DEBUG, "dela:hdfs:readme"); - return result; - } - - private ManifestJSON createManifest(Project project, Dataset dataset, Users user) throws DelaException { - String hdfsUser = hdfsUsersBean.getHdfsUserName(project, user); - DistributedFileSystemOps dfso = dfs.getDfsOps(hdfsUser); - Path datasetPath = datasetCtrl.getDatasetPath(dataset); - - ManifestJSON manifest = new ManifestJSON(); - manifest.setDatasetName(dataset.getName()); - manifest.setDatasetDescription(dataset.getDescription()); - manifest.setKafkaSupport(false); - - List datasetFiles = new LinkedList<>(); - Map avroFiles = new HashMap<>(); - for (Inode i : inodeController.getChildren(dataset.getInode())) { - if (i.isDir()) { - throw new DelaException(RESTCodes.DelaErrorCode.SUBDIRS_NOT_SUPPORTED, Level.FINE, DelaException.Source.LOCAL); - } - if (isAvro(i.getInodePK().getName())) { - avroFiles.put(i.getInodePK().getName(), i); - } else { - datasetFiles.add(i); - } - } - - List fileInfos = new LinkedList<>(); - for (Inode i : datasetFiles) { - String fileName = i.getInodePK().getName(); - FileInfo fileInfo = new FileInfo(); - fileInfo.setFileName(fileName); - Path filePath = new Path(datasetPath, fileName); - try { - fileInfo.setLength(dfso.getLength(filePath)); - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, null, - ex.getMessage(), ex); - } - if (avroFiles.containsKey(fileName + ".avro")) { - Path avroSchemaPath = new Path(datasetPath, filePath + ".avro"); - fileInfo.setSchema(new String(read(project, user, avroSchemaPath))); - manifest.setKafkaSupport(true); - } else { - fileInfo.setSchema(""); - } - fileInfos.add(fileInfo); - } - for (Inode i : avroFiles.values()) { - String fileName = i.getInodePK().getName(); - FileInfo fileInfo = new FileInfo(); - fileInfo.setFileName(fileName); - fileInfo.setSchema(""); - String filePath = datasetPath + File.separator + fileName; - fileInfo.setLength(dfso.getlength(filePath)); - fileInfos.add(fileInfo); - } - manifest.setFileInfos(fileInfos); - - DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); - manifest.setCreatorDate(dateFormat.format(new Date())); - manifest.setCreatorEmail(user.getEmail()); - - //TODO other schemas - manifest.setMetaDataJsons(new ArrayList<>()); - return manifest; - } - - public void write(Project project, Users user, Path filePath, byte[] fileContent) throws DelaException { - - String hdfsUser = hdfsUsersBean.getHdfsUserName(project, user); - DistributedFileSystemOps dfso = dfs.getDfsOps(hdfsUser); - try { - if (dfso.exists(filePath)) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.FINE, DelaException.Source.HDFS, - "file exists"); - } - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, - "cannot read", ex.getMessage(), ex); - } - - FSDataOutputStream out = null; - try { - out = dfso.create(filePath); - out.write(fileContent); - out.flush(); - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, - "cannot write", ex.getMessage(), ex); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, - "cannot close", ex.getMessage(), ex); - } - } - } - } - - public byte[] read(Project project, Users user, Path filePath) throws DelaException { - String hdfsUser = hdfsUsersBean.getHdfsUserName(project, user); - DistributedFileSystemOps dfso = dfs.getDfsOps(hdfsUser); - byte[] result = read(dfso, filePath); - return result; - } - - public byte[] read(DistributedFileSystemOps dfso, Path filePath) throws DelaException { - try { - if (!dfso.exists(filePath)) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.FINE, DelaException.Source.HDFS, - "file does not exist"); - } - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, - "cannot read", ex.getMessage(), ex); - } - - FSDataInputStream fdi = null; - try { - fdi = dfso.open(filePath); - long fileLength = dfso.getLength(filePath); - byte[] fileContent = new byte[(int) fileLength]; - fdi.readFully(fileContent); - return fileContent; - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, - "cannot read", ex.getMessage(), ex); - } finally { - if (fdi != null) { - try { - fdi.close(); - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, - "cannot close", ex.getMessage(), ex); - } - } - } - } - - public void delete(Project project, Users user, Path filePath) throws DelaException { - - String hdfsUser = hdfsUsersBean.getHdfsUserName(project, user); - DistributedFileSystemOps dfso = dfs.getDfsOps(hdfsUser); - try { - if (!dfso.exists(filePath)) { - return; - } - dfso.rm(filePath, true); - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, - "cannot delete", ex.getMessage(), ex); - } - } - - public long length(Project project, Users user, Path filePath) throws DelaException { - String hdfsUser = hdfsUsersBean.getHdfsUserName(project, user); - DistributedFileSystemOps dfso = dfs.getDfsOps(hdfsUser); - try { - if (!dfso.exists(filePath)) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.FINE, DelaException.Source.HDFS, - "dataset does not exist"); - } - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, - "cannot read", ex.getMessage(), ex); - } - - try { - long fileLength = dfso.getDatasetSize(filePath); - return fileLength; - } catch (IOException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.ACCESS_ERROR, Level.SEVERE, DelaException.Source.HDFS, - "cannot read dataset", ex.getMessage(), ex); - } - } - - private boolean isAvro(String s) { - String remove_spaces = s.replaceAll(" ", ""); - String[] split = remove_spaces.split("\\."); - if (split.length == 2) { - return split[1].equals("avro"); - } else { - return false; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaSetupWorker.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaSetupWorker.java deleted file mode 100644 index e7a2fd5788..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaSetupWorker.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.hops.hopsworks.dela; - -import com.google.gson.Gson; -import io.hops.hopsworks.common.dao.dela.certs.ClusterCertificateFacade; -import io.hops.hopsworks.common.dela.AddressJSON; -import io.hops.hopsworks.restutils.RESTCodes; -import io.hops.hopsworks.common.security.CertificatesMgmService; -import io.hops.hopsworks.common.util.OSProcessExecutor; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.dto.hopssite.ClusterServiceDTO; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.dela.hopssite.HopssiteController; -import io.hops.hopsworks.util.CertificateHelper; -import io.hops.hopsworks.util.SettingsHelper; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.security.KeyStore; -import java.util.List; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.annotation.Resource; -import javax.ejb.EJB; -import javax.ejb.Singleton; -import javax.ejb.Startup; -import javax.ejb.Timeout; -import javax.ejb.Timer; -import javax.ejb.TimerService; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import org.apache.commons.codec.digest.DigestUtils; -import org.javatuples.Pair; -import org.javatuples.Triplet; - -@Startup -@Singleton -public class DelaSetupWorker { - - private static final Logger LOGGER = Logger.getLogger(DelaSetupWorker.class.getName()); - - @Resource - TimerService timerService; - @EJB - private Settings settings; - @EJB - private ClusterCertificateFacade clusterCertFacade; - @EJB - private DelaStateController delaStateCtrl; - @EJB - private HopssiteController hopsSiteProxy; - @EJB - private TransferDelaController delaCtrl; - @EJB - private CertificatesMgmService certificatesMgmService; - @EJB - private OSProcessExecutor osProcessExecutor; - - private State state; - //5 required to get from start to running in perfect mode - private int tryTimeouts = 0; - private boolean healthy = true; - /** - * after 20 tries without reaching last state(active - running), backoff and try a bit less often, there is something - * obviously wrong with some other third party service. .Give it time to regenerate - */ - private static int TRY_TIMERS_BACKOFF = 20; - - @PostConstruct - private void init() { - if (delaStateCtrl.delaEnabled()) { - state = State.SETUP; - timerService.createTimer(0, settings.getHOPSSITE_HEARTBEAT_RETRY(), "Timer for dela settings check."); - LOGGER.log(Level.INFO, "{0} - state:{1}", new Object[]{DelaException.Source.HOPS_SITE, state}); - } - } - - @PreDestroy - private void destroyTimer() { - for (Timer timer : timerService.getTimers()) { - timer.cancel(); - } - } - - private Timer resetTimer(Timer timer, long intervalDuration) { - timer.cancel(); - return timerService.createTimer(intervalDuration, intervalDuration, "Timer for " + state + "."); - } - - @Timeout - @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) - private void timeout(Timer timer) { - try { - LOGGER.log(Level.INFO, "{0} - state:{1} timeout:{2}", - new Object[]{DelaException.Source.HOPS_SITE, state, timer.getInfo().toString()}); - switch (state) { - case SETUP: - setup(timer); - break; - case DELA_VERSION: - delaVersion(timer); - break; - case DELA_CONTACT: - delaContact(timer); - break; - case REGISTER: - hopsSiteRegister(timer); - break; - case HEAVY_PING: - heavyPing(timer); - break; - case PING: - ping(timer); - break; - default: - throw new IllegalStateException("unknown state"); - } - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Got an exception during timeout"); - } - } - - //******************************************************************************************************************** - private void setup(Timer timer) { - Optional masterPswd = settings.getHopsSiteClusterPswd(); - if (!masterPswd.isPresent()) { - //TODO Alex - use the registration pswd hash once the admin UI is ready - String pswd = DigestUtils.sha256Hex(settings.getHopsSiteClusterPswdAux()); - settings.setHopsSiteClusterPswd(pswd); - masterPswd = settings.getHopsSiteClusterPswd(); - } - Optional clusterName = settings.getHopsSiteClusterName(); - - if (clusterName.isPresent()) { - Optional> keystoreAux - = CertificateHelper.loadKeystoreFromDB(masterPswd.get(), clusterName.get(), clusterCertFacade, - certificatesMgmService); - if (keystoreAux.isPresent()) { - setupComplete(keystoreAux.get(), timer); - return; - } - } - - Optional> keystoreAux - = CertificateHelper.loadKeystoreFromFile(masterPswd.get(), settings, clusterCertFacade, certificatesMgmService, - osProcessExecutor); - if (keystoreAux.isPresent()) { - setupComplete(keystoreAux.get(), timer); - } else { - LOGGER.log(Level.WARNING, "{0} - dela setup not ready - certificates not ready", - new Object[]{DelaException.Source.HOPS_SITE}); - } - } - - private void setupComplete(Triplet keystoreAux, Timer timer) { - KeyStore keystore = keystoreAux.getValue0(); - KeyStore truststore = keystoreAux.getValue1(); - String certPswd = keystoreAux.getValue2(); - delaStateCtrl.hopssiteCertsAvailable(keystore, truststore, certPswd); - timer = resetToDelaVersion(timer); - delaVersion(timer); - } - - private String delaVersion; - - private void delaVersion(Timer timer) { - LOGGER.log(Level.INFO, "{0} - retrieving hops-site dela_version", new Object[]{DelaException.Source.HOPS_SITE}); - try { - delaVersion = hopsSiteProxy.delaVersion(); - delaStateCtrl.hopssiteContacted(); - timer = resetToDelaContact(timer); - delaContact(timer); - } catch (DelaException tpe) { - LOGGER.log(Level.WARNING, "{0} - source:<{1}>:{2}", - new Object[]{DelaException.Source.HOPS_SITE, tpe.getSource(), tpe.getMessage()}); - timer = tryAgainLater(timer); - } - } - - private void delaContact(Timer timer) { - LOGGER.log(Level.INFO, "{0} - state:{1}", new Object[]{DelaException.Source.HOPS_SITE, state}); - AddressJSON delaTransferEndpoint; - try { - delaTransferEndpoint = SettingsHelper.delaTransferEndpoint(settings); - } catch (DelaException tpe) { - try { - delaTransferEndpoint = getDelaTransferEndpoint(delaVersion); - delaStateCtrl.transferDelaContacted(); - settings.setDELA_PUBLIC_ENDPOINT(delaTransferEndpoint); - timer = resetToRegister(timer); - hopsSiteRegister(timer, delaTransferEndpoint); - } catch (DelaException tpe2) { - LOGGER.log(Level.WARNING, "{0} - source:<{1}>:{2}", new Object[]{DelaException.Source.HOPS_SITE, - tpe2.getSource(), tpe2.getMessage()}); - timer = tryAgainLater(timer); - } - } - - } - - private void hopsSiteRegister(Timer timer) { - LOGGER.log(Level.INFO, "{0} - state:{1}", new Object[]{DelaException.Source.HOPS_SITE, state}); - AddressJSON delaTransferEndpoint; - try { - delaTransferEndpoint = SettingsHelper.delaTransferEndpoint(settings); - } catch (DelaException ex) { - timer = resetToDelaContact(timer); - return; - } - try { - hopsSiteRegister(timer, delaTransferEndpoint); - } catch (DelaException tpe) { - LOGGER.log(Level.WARNING, "{0} - source:<{1}>:{2}", - new Object[]{DelaException.Source.HOPS_SITE, tpe.getSource(), tpe.getMessage()}); - timer = tryAgainLater(timer); - } - } - - private void hopsSiteRegister(Timer timer, AddressJSON delaTransferEndpoint) throws DelaException { - String delaHttpEndpoint = SettingsHelper.delaHttpEndpoint(settings); - hopsSiteRegister(timer, delaHttpEndpoint, delaTransferEndpoint); - } - - private void hopsSiteRegister(Timer timer, String delaClusterAddress, AddressJSON delaTransferAddress) - throws DelaException { - String publicCId = hopsSiteProxy.registerCluster(delaClusterAddress, new Gson().toJson(delaTransferAddress)); - settings.setDELA_CLUSTER_ID(publicCId); - timer = resetToHeavyPing(timer); - heavyPing(timer); - } - - private void heavyPing(Timer timer) { - LOGGER.log(Level.INFO, "{0} - state:{1}", new Object[]{DelaException.Source.HOPS_SITE, state}); - Pair, List> datasets; - try { - datasets = delaCtrl.getContents(); - } catch (DelaException tpe) { - LOGGER.log(Level.WARNING, "{0} - source:<{1}>:{2}", - new Object[]{DelaException.Source.HOPS_SITE, tpe.getSource(), tpe.getMessage()}); - if (DelaException.Source.SETTINGS.equals(tpe.getSource())) { - timer = resetToDelaContact(timer); - } else { - timer = tryAgainLater(timer); - } - return; - } - try { - hopsSiteProxy.heavyPing(datasets.getValue0(), datasets.getValue1()); - timer = resetToPing(timer); - ping(timer); - } catch (DelaException tpe) { - LOGGER.log(Level.WARNING, "{0} - source:<{1}>:{2}", - new Object[]{DelaException.Source.HOPS_SITE, tpe.getSource(), tpe.getMessage()}); - if (RESTCodes.DelaErrorCode.CLUSTER_NOT_REGISTERED.getMessage().equals(tpe.getMessage())) { - timer = resetToRegister(timer); - } else if (DelaException.Source.SETTINGS.equals(tpe.getSource())) { - timer = resetToRegister(timer); - } else { - timer = tryAgainLater(timer); - } - } - } - - private void ping(Timer timer) { - LOGGER.log(Level.INFO, "{0} - state:{1}", new Object[]{DelaException.Source.HOPS_SITE, state}); - Pair, List> datasets; - try { - datasets = delaCtrl.getContents(); - } catch (DelaException tpe) { - LOGGER.log(Level.WARNING, "{0} - source:<{1}>:{2}", - new Object[]{DelaException.Source.HOPS_SITE, tpe.getSource(), tpe.getMessage()}); - if (DelaException.Source.SETTINGS.equals(tpe.getSource())) { - timer = resetToDelaContact(timer); - } else { - timer = tryAgainLater(timer); - } - return; - } - try { - hopsSiteProxy.ping(new ClusterServiceDTO.Ping(datasets.getValue0().size(), datasets.getValue1().size())); - if (!healthy) { - timer = healthyPing(timer); - } - } catch (DelaException tpe) { - LOGGER.log(Level.WARNING, "{0} - source:<{1}>:{2}", - new Object[]{DelaException.Source.HOPS_SITE, tpe.getSource(), tpe.getMessage()}); - if (RESTCodes.DelaErrorCode.CLUSTER_NOT_REGISTERED.getMessage().equals(tpe.getMessage())) { - timer = resetToRegister(timer); - } else if (RESTCodes.DelaErrorCode.HEAVY_PING.getMessage().equals(tpe.getMessage())) { - timer = resetToHeavyPing(timer); - } else if (DelaException.Source.SETTINGS.equals(tpe.getSource())) { - timer = resetToDelaContact(timer); - } else { - timer = tryAgainLater(timer); - } - } - } - - private Timer tryTimer(Timer timer, boolean health) { - this.healthy = health; - long intervalDuration; - if (this.healthy) { - tryTimeouts = 0; - intervalDuration = settings.getHOPSSITE_HEARTBEAT_INTERVAL(); - } else { - tryTimeouts++; - if (tryTimeouts < TRY_TIMERS_BACKOFF) { - intervalDuration = settings.getHOPSSITE_HEARTBEAT_RETRY(); - } else { - //backoff and try again after a longer timeout - intervalDuration = settings.getHOPSSITE_HEARTBEAT_INTERVAL(); - } - } - return resetTimer(timer, intervalDuration); - } - - private Timer tryAgainLater(Timer timer) { - return tryTimer(timer, false); - } - - private Timer resetToSettings(Timer timer) { - state = State.SETUP; - return tryTimer(timer, false); - } - - private Timer resetToDelaVersion(Timer timer) { - LOGGER.log(Level.WARNING, "{0} - reset from:{1} to {2}", - new Object[]{DelaException.Source.HOPS_SITE, state, State.DELA_VERSION}); - state = State.DELA_VERSION; - return tryTimer(timer, false); - } - - private Timer resetToDelaContact(Timer timer) { - LOGGER.log(Level.WARNING, "{0} - reset from:{1} to {2}", - new Object[]{DelaException.Source.HOPS_SITE, state, State.DELA_CONTACT}); - state = State.DELA_CONTACT; - return tryTimer(timer, false); - } - - private Timer resetToRegister(Timer timer) { - LOGGER.log(Level.WARNING, "{0} - reset from:{1} to {2}", - new Object[]{DelaException.Source.HOPS_SITE, state, State.REGISTER}); - state = State.REGISTER; - return tryTimer(timer, false); - } - - private Timer resetToHeavyPing(Timer timer) { - LOGGER.log(Level.WARNING, "{0} - reset from:{1} to {2}", - new Object[]{DelaException.Source.HOPS_SITE, state, State.HEAVY_PING}); - state = State.HEAVY_PING; - return tryTimer(timer, false); - } - - private Timer resetToPing(Timer timer) { - LOGGER.log(Level.WARNING, "{0} - reset from:{1} to {2}", - new Object[]{DelaException.Source.HOPS_SITE, state, State.PING}); - state = State.PING; - return tryTimer(timer, false); - } - - private Timer healthyPing(Timer timer) { - state = State.PING; - return tryTimer(timer, true); - } - - private String getDelaTransferHttpEndpoint() throws HeartbeatException { - String delaHttpEndpoint = settings.getDELA_TRANSFER_HTTP_ENDPOINT(); - try { - URL u = new URL(delaHttpEndpoint); - u.toURI(); - // validate url syntax - if (u.getHost().isEmpty() || u.getPort() == -1) { - LOGGER.log(Level.WARNING, "{0} - malformed dela url. {1} - reset in database", - new Object[]{DelaException.Source.HOPS_SITE, delaHttpEndpoint}); - throw new HeartbeatException("DELA_TRANSFER_HTTP"); - } - return delaHttpEndpoint; - } catch (MalformedURLException | URISyntaxException ex) { - LOGGER.log(Level.SEVERE, "{1} - malformed dela url. {1} - reset in database", - new Object[]{DelaException.Source.HOPS_SITE, delaHttpEndpoint}); - throw new HeartbeatException("DELA_TRANSFER_HTTP"); - } - } - - private AddressJSON getDelaTransferEndpoint(String delaVersion) throws DelaException { - String delaTransferHttpEndpoint = SettingsHelper.delaTransferHttpEndpoint(settings); - LOGGER.log(Level.INFO, "{0} - dela http endpoint: {1}", - new Object[]{DelaException.Source.HOPS_SITE, delaTransferHttpEndpoint}); - AddressJSON delaTransferEndpoint = delaCtrl.getDelaPublicEndpoint(delaVersion); - LOGGER.log(Level.INFO, "{0} - dela transfer endpoint: {1}", - new Object[]{DelaException.Source.HOPS_SITE, delaTransferEndpoint.toString()}); - return delaTransferEndpoint; - - } - - private static class HeartbeatException extends Exception { - - public HeartbeatException(String msg) { - super(msg); - } - } - - private static enum State { - - SETUP, - DELA_VERSION, - DELA_CONTACT, - REGISTER, - HEAVY_PING, - PING - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaStateController.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaStateController.java deleted file mode 100644 index 2be3db922e..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaStateController.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela; - -import io.hops.hopsworks.restutils.RESTCodes; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.exceptions.DelaException; -import java.security.KeyStore; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.PostConstruct; -import javax.ejb.EJB; -import javax.ejb.Singleton; -import javax.ejb.Startup; - -@Startup -@Singleton -public class DelaStateController { - - private static final Logger LOGGER = Logger.getLogger(DelaStateController.class.getName()); - - @EJB - private Settings settings; - - private boolean delaEnabled = false; - private boolean delaCertsAvailable = false; - private boolean transferDelaAvailable = false; - private boolean hopssiteAvailable = false; - - private KeyStore keystore; - private KeyStore truststore; - private String keystorePassword; - - @PostConstruct - private void init() { - if (settings.isDelaEnabled()) { - delaEnabled = settings.isDelaEnabled(); - LOGGER.log(Level.INFO, "dela enabled"); - } else { - LOGGER.log(Level.INFO, "dela disabled"); - } - } - - public boolean delaEnabled() { - return delaEnabled; - } - - public boolean delaAvailable() { - return hopsworksDelaSetup() && transferDelaAvailable && hopssiteAvailable; - } - - public void checkDelaAvailable() throws DelaException { - if (!delaAvailable()) { - throw new DelaException(RESTCodes.DelaErrorCode.DELA_NOT_AVAILABLE, Level.SEVERE, DelaException.Source.LOCAL); - } - } - - public boolean transferDelaAvailable() { - return hopsworksDelaSetup() && transferDelaAvailable; - } - - public boolean hopssiteAvailable() { - return hopsworksDelaSetup() && hopssiteAvailable; - } - - public void checkHopssiteAvailable() throws DelaException { - if (!hopssiteAvailable()) { - throw new DelaException(RESTCodes.DelaErrorCode.HOPSSITE_NOT_AVAILABLE, Level.SEVERE, DelaException.Source.LOCAL); - } - } - - public boolean hopsworksDelaSetup() { - return delaEnabled && delaCertsAvailable; - } - - public void checkHopsworksDelaSetup() throws DelaException { - if (!hopsworksDelaSetup()) { - throw new DelaException(RESTCodes.DelaErrorCode.REMOTE_DELA_NOT_AVAILABLE, Level.SEVERE, - DelaException.Source.LOCAL); - } - } - - public void transferDelaContacted() { - transferDelaAvailable = true; - } - - public void hopssiteContacted() { - hopssiteAvailable = true; - } - - public void hopssiteCertsAvailable(KeyStore keystore, KeyStore truststore, String keystorePassword) { - delaCertsAvailable = true; - this.keystore = keystore; - this.truststore = truststore; - this.keystorePassword = keystorePassword; - } - - public KeyStore getKeystore() { - return keystore; - } - - public KeyStore getTruststore() { - return truststore; - } - - public String getKeystorePassword() { - return keystorePassword; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaWorkerController.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaWorkerController.java deleted file mode 100644 index 0f7ab4b009..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/DelaWorkerController.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela; - -import io.hops.hopsworks.common.dataset.DatasetController; -import io.hops.hopsworks.common.hdfs.HdfsUsersController; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.dto.hopsworks.HopsworksTransferDTO; -import io.hops.hopsworks.dela.hopssite.HopsSite; -import io.hops.hopsworks.dela.hopssite.HopssiteController; -import io.hops.hopsworks.dela.old_dto.ExtendedDetails; -import io.hops.hopsworks.dela.old_dto.HDFSEndpoint; -import io.hops.hopsworks.dela.old_dto.HDFSResource; -import io.hops.hopsworks.dela.old_dto.HdfsDetails; -import io.hops.hopsworks.dela.old_dto.HopsDatasetDetailsDTO; -import io.hops.hopsworks.dela.old_dto.KafkaDetails; -import io.hops.hopsworks.dela.old_dto.KafkaEndpoint; -import io.hops.hopsworks.dela.old_dto.KafkaResource; -import io.hops.hopsworks.dela.old_dto.ManifestJSON; -import io.hops.hopsworks.exceptions.DatasetException; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.exceptions.ProvenanceException; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import io.hops.hopsworks.persistence.entity.project.Project; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.restutils.RESTCodes; -import org.apache.hadoop.fs.Path; -import org.json.JSONException; -import org.json.JSONObject; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import java.io.IOException; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Stateless -@TransactionAttribute(TransactionAttributeType.NEVER) -public class DelaWorkerController { - - private final Logger LOGGER = Logger.getLogger(DelaWorkerController.class.getName()); - - @EJB - private DatasetController datasetCtrl; - @EJB - private Settings settings; - @EJB - private DelaStateController delaStateCtrl; - @EJB - private TransferDelaController delaCtrl; - @EJB - private HopssiteController hopsSiteCtrl; - @EJB - private DelaDatasetController delaDatasetCtrl; - @EJB - private DelaHdfsController delaHdfsCtrl; - @EJB - private HdfsUsersController hdfsUsersBean; - - public String shareDatasetWithHops(Project project, Dataset dataset, Users user) throws DelaException { - - if (dataset.isPublicDs()) { - return dataset.getPublicDsId(); - } - if (dataset.isShared(project)) { - throw new DelaException(RESTCodes.DelaErrorCode.DATASET_PUBLISH_PERMISSION_ERROR, Level.WARNING, - DelaException.Source.LOCAL); - } - delaStateCtrl.checkDelaAvailable(); - delaHdfsCtrl.writeManifest(project, dataset, user); - - long datasetSize = delaHdfsCtrl.datasetSize(project, dataset, user); - String publicDSId; - try { - publicDSId = hopsSiteCtrl.performAsUser(user, new HopsSite.UserFunc() { - @Override - public String perform() throws DelaException { - return hopsSiteCtrl.publish(dataset.getName(), dataset.getDescription(), getCategories(), - datasetSize, user.getEmail()); - - } - }); - delaCtrlUpload(project, dataset, user, publicDSId); - } catch (DelaException tpe) { - if (DelaException.Source.HOPS_SITE.equals(tpe.getSource()) - && RESTCodes.DelaErrorCode.DATASET_EXISTS.equals(tpe.getMessage())) { - //TODO ask dela to checksum it; - } - throw tpe; - } - delaDatasetCtrl.uploadToHops(project, dataset, publicDSId); - LOGGER.log(Level.INFO, "{0} shared with hops", publicDSId); - return publicDSId; - } - - private void delaCtrlUpload(Project project, Dataset dataset, Users user, String publicDSId) - throws DelaException { - Path datasetPath = datasetCtrl.getDatasetPath(dataset); - HDFSResource resource = new HDFSResource(datasetPath.toString(), Settings.MANIFEST_FILE); - String hdfsUser = hdfsUsersBean.getHdfsUserName(project, user); - HDFSEndpoint endpoint = new HDFSEndpoint(getHDFSXmlPath().toString(), hdfsUser); - HopsDatasetDetailsDTO details = new HopsDatasetDetailsDTO(dataset.getName(), project.getId(), dataset.getId()); - delaCtrl.upload(publicDSId, details, resource, endpoint); - } - - public void unshareFromHops(Project project, Dataset dataset, Users user) throws DelaException { - if (!dataset.isPublicDs()) { - return; - } - delaStateCtrl.checkDelaAvailable(); - delaCtrl.cancel(dataset.getPublicDsId()); - hopsSiteCtrl.cancel(dataset.getPublicDsId()); - delaHdfsCtrl.deleteManifest(project, dataset, user); - delaDatasetCtrl.unshareFromHops(project, dataset); - } - - public void unshareFromHopsAndClean(Project project, Dataset dataset, Users user) throws DelaException, IOException, - DatasetException { - unshareFromHops(project, dataset, user); - delaDatasetCtrl.delete(project, dataset, user); - } - - public ManifestJSON startDownload(Project project, Users user, HopsworksTransferDTO.Download downloadDTO) - throws DelaException, DatasetException, ProvenanceException, IOException { - delaStateCtrl.checkDelaAvailable(); - Dataset dataset = delaDatasetCtrl.download(project, user, downloadDTO.getPublicDSId(), downloadDTO.getName()); - - try { - delaCtrlStartDownload(project, dataset, user, downloadDTO); - } catch (DelaException tpe) { - delaDatasetCtrl.delete(project, dataset, user); - throw tpe; - } - - ManifestJSON manifest = delaHdfsCtrl.readManifest(project, dataset, user); - delaDatasetCtrl.updateDescription(project, dataset, manifest.getDatasetDescription()); - return manifest; - } - - private void delaCtrlStartDownload(Project project, Dataset dataset, Users user, - HopsworksTransferDTO.Download downloadDTO) throws DelaException { - Path datasetPath = datasetCtrl.getDatasetPath(dataset); - HDFSResource resource = new HDFSResource(datasetPath.toString(), Settings.MANIFEST_FILE); - String hdfsUser = hdfsUsersBean.getHdfsUserName(project, user); - HDFSEndpoint endpoint = new HDFSEndpoint(getHDFSXmlPath().toString(), hdfsUser); - HopsDatasetDetailsDTO details = new HopsDatasetDetailsDTO(downloadDTO.getName(), project.getId(), dataset.getId()); - delaCtrl.startDownload(downloadDTO.getPublicDSId(), details, resource, endpoint, downloadDTO.getBootstrap()); - } - - public void advanceDownload(Project project, Dataset dataset, Users user, HopsworksTransferDTO.Download downloadDTO, - String sessionId, KafkaEndpoint kafkaEndpoint) throws DelaException { - - delaStateCtrl.checkDelaAvailable(); - delaCtrlAdvanceDownload(project, dataset, user, downloadDTO, sessionId, kafkaEndpoint); - hopsSiteCtrl.download(downloadDTO.getPublicDSId()); - } - - private void delaCtrlAdvanceDownload(Project project, Dataset dataset, Users user, - HopsworksTransferDTO.Download downloadDTO, String sessionId, KafkaEndpoint kafkaEndpoint) - throws DelaException { - Path datasetPath = datasetCtrl.getDatasetPath(dataset); - JSONObject fileTopics = new JSONObject(downloadDTO.getTopics()); - LinkedList hdfsResources = new LinkedList<>(); - LinkedList kafkaResources = new LinkedList<>(); - - Iterator iter = fileTopics.keys(); - while (iter.hasNext()) { - String key = iter.next(); - try { - String value = (String) fileTopics.get(key); - if (!value.equals("") && kafkaEndpoint != null) { - kafkaResources.add(new KafkaDetails(key, new KafkaResource(sessionId, value))); - } - hdfsResources.add(new HdfsDetails(key, new HDFSResource(datasetPath.toString(), key))); - } catch (JSONException e) { - // Something went wrong! - } - } - String hdfsUser = hdfsUsersBean.getHdfsUserName(project, user); - HDFSEndpoint hdfsEndpoint = new HDFSEndpoint(getHDFSXmlPath().toString(), hdfsUser); - ExtendedDetails details = new ExtendedDetails(hdfsResources, kafkaResources); - delaCtrl.advanceDownload(downloadDTO.getPublicDSId(), hdfsEndpoint, kafkaEndpoint, details); - } - - //******************************************************************************************************************** - - private Collection getCategories() { - Set categories = new HashSet<>(); - return categories; - } - - private Path getHDFSXmlPath() { - return new Path(settings.getHadoopConfDir(), Settings.DEFAULT_HADOOP_CONFFILE_NAME); - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/RemoteDelaController.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/RemoteDelaController.java deleted file mode 100644 index bf01a9d4f9..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/RemoteDelaController.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela; - -import io.hops.hopsworks.common.dataset.FilePreviewDTO; -import io.hops.hopsworks.restutils.RESTCodes; -import io.hops.hopsworks.common.util.ClientWrapper; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.dto.common.ClusterAddressDTO; -import io.hops.hopsworks.exceptions.DelaException; - -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.ejb.EJB; -import javax.ejb.Stateless; - -@Stateless -public class RemoteDelaController { - - private static final Logger LOGGER = Logger.getLogger(RemoteDelaController.class.getName()); - - @EJB - private Settings settings; - @EJB - private DelaStateController delaStateCtrl; - - private void checkReady() throws DelaException { - delaStateCtrl.checkHopsworksDelaSetup(); - } - - //******************************************************************************************************************** - public FilePreviewDTO readme(String publicDSId, ClusterAddressDTO source) throws DelaException { - checkReady(); - try { - ClientWrapper client = getClient(source.getDelaClusterAddress(), Path.readme(publicDSId), FilePreviewDTO.class); - LOGGER.log(Settings.DELA_DEBUG, "dela:cross:readme {0}", client.getFullPath()); - FilePreviewDTO result = (FilePreviewDTO) client.doGet(); - LOGGER.log(Settings.DELA_DEBUG, "dela:cross:readme:done {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.REMOTE_DELA, null, ex.getMessage(), ex); - } - } - - private ClientWrapper getClient(String delaClusterAddress, String path, Class resultClass) - throws DelaException { - return ClientWrapper.httpsInstance(resultClass).setTarget(delaClusterAddress).setPath(path); - } - - public static class Path { - - public static String readme(String publicDSId) { - return "/remote/dela/datasets/" + publicDSId + "/readme"; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/TransferDela.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/TransferDela.java deleted file mode 100644 index 7cc476a3d3..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/TransferDela.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela; - -public class TransferDela { - public static final String CONTACT = "/vod/endpoint"; -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/TransferDelaController.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/TransferDelaController.java deleted file mode 100644 index fc01abdc3a..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/TransferDelaController.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela; - -import com.google.gson.Gson; -import io.hops.hopsworks.common.dela.AddressJSON; -import io.hops.hopsworks.restutils.RESTCodes; -import io.hops.hopsworks.common.util.ClientWrapper; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.dto.common.ClusterAddressDTO; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.dela.old_dto.ElementSummaryJSON; -import io.hops.hopsworks.dela.old_dto.ExtendedDetails; -import io.hops.hopsworks.dela.old_dto.HDFSEndpoint; -import io.hops.hopsworks.dela.old_dto.HDFSResource; -import io.hops.hopsworks.dela.old_dto.HopsContentsReqJSON; -import io.hops.hopsworks.dela.old_dto.HopsContentsSummaryJSON; -import io.hops.hopsworks.dela.old_dto.HopsDatasetDetailsDTO; -import io.hops.hopsworks.dela.old_dto.HopsTorrentAdvanceDownload; -import io.hops.hopsworks.dela.old_dto.HopsTorrentStartDownload; -import io.hops.hopsworks.dela.old_dto.HopsTorrentUpload; -import io.hops.hopsworks.dela.old_dto.KafkaEndpoint; -import io.hops.hopsworks.dela.old_dto.SuccessJSON; -import io.hops.hopsworks.dela.old_dto.TorrentExtendedStatusJSON; -import io.hops.hopsworks.dela.old_dto.TorrentId; -import io.hops.hopsworks.util.SettingsHelper; -import org.javatuples.Pair; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import java.util.LinkedList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Stateless -@TransactionAttribute(TransactionAttributeType.NEVER) -public class TransferDelaController { - - private Logger logger = Logger.getLogger(TransferDelaController.class.getName()); - @EJB - private Settings settings; - @EJB - private DelaStateController delaStateController; - - public AddressJSON getDelaPublicEndpoint(String delaVersion) throws DelaException { - String delaTransferHttpEndpoint = SettingsHelper.delaTransferHttpEndpoint(settings); - try { - ClientWrapper rc = ClientWrapper - .httpInstance(AddressJSON.class) - .setTarget(delaTransferHttpEndpoint) - .setPath(TransferDela.CONTACT) - .setPayload(delaVersion); - logger.log(Settings.DELA_DEBUG, "dela:contact {0}", rc.getFullPath()); - AddressJSON result = rc.doPost(); - logger.log(Settings.DELA_DEBUG, "dela:contact - done {0} {1}", new Object[]{rc.getFullPath(), result.getIp()}); - return result; - } catch (IllegalStateException ise) { - logger.log(Level.WARNING, "dela:contact - communication fail{0}", ise.getMessage()); - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.DELA, null, ise.getMessage(), ise); - } - } - - public void upload(String publicDSId, HopsDatasetDetailsDTO datasetDetails, HDFSResource resource, - HDFSEndpoint endpoint) throws DelaException { - if(!delaStateController.transferDelaAvailable()) { - throw new DelaException(RESTCodes.DelaErrorCode.DELA_TRANSFER_NOT_AVAILABLE, Level.SEVERE, - DelaException.Source.LOCAL); - } - logger.log(Settings.DELA_DEBUG, "{0} upload - transfer"); - HopsTorrentUpload reqContent = new HopsTorrentUpload(new TorrentId(publicDSId), datasetDetails.getDatasetName(), - datasetDetails.getProjectId(), datasetDetails.getDatasetId(), resource, endpoint); - try { - ClientWrapper rc = ClientWrapper - .httpInstance(SuccessJSON.class) - .setTarget(settings.getDELA_TRANSFER_HTTP_ENDPOINT()) - .setPath("torrent/hops/upload/xml") - .setPayload(reqContent); - SuccessJSON result = rc.doPost(); - } catch (IllegalStateException ise) { - logger.log(Level.WARNING, "dela communication fail:{0}", ise.getMessage()); - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.DELA, null, ise.getMessage(), ise); - } - } - - public void startDownload(String publicDSId, HopsDatasetDetailsDTO datasetDetails, HDFSResource resource, - HDFSEndpoint endpoint, List bootstrap) - throws DelaException { - - if(!delaStateController.transferDelaAvailable()) { - throw new DelaException(RESTCodes.DelaErrorCode.DELA_TRANSFER_NOT_AVAILABLE, Level.SEVERE, - DelaException.Source.LOCAL); - } - List bootstrapAdr = new LinkedList<>(); - Gson gson = new Gson(); - for(ClusterAddressDTO b : bootstrap) { - bootstrapAdr.add(gson.fromJson(b.getDelaTransferAddress(), AddressJSON.class)); - } - HopsTorrentStartDownload reqContent = new HopsTorrentStartDownload(new TorrentId(publicDSId), datasetDetails. - getDatasetName(), datasetDetails.getProjectId(), datasetDetails.getDatasetId(), resource, bootstrapAdr, endpoint); - try { - ClientWrapper rc = ClientWrapper - .httpInstance(SuccessJSON.class) - .setTarget(settings.getDELA_TRANSFER_HTTP_ENDPOINT()) - .setPath("torrent/hops/download/start/xml") - .setPayload(reqContent); - SuccessJSON result = rc.doPost(); - } catch (IllegalStateException ise) { - logger.log(Level.WARNING, "dela communication fail:{0}", ise.getMessage()); - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.DELA, null, ise.getMessage(), ise); - } - } - - public void advanceDownload(String publicDSId, HDFSEndpoint hdfsEndpoint, KafkaEndpoint kafkaEndpoint, - ExtendedDetails details) - throws DelaException { - - if(!delaStateController.transferDelaAvailable()) { - throw new DelaException(RESTCodes.DelaErrorCode.DELA_TRANSFER_NOT_AVAILABLE, Level.SEVERE, - DelaException.Source.LOCAL); - } - - HopsTorrentAdvanceDownload reqContent = new HopsTorrentAdvanceDownload(new TorrentId(publicDSId), - kafkaEndpoint, hdfsEndpoint, details); - try { - ClientWrapper rc = ClientWrapper - .httpInstance(SuccessJSON.class) - .setTarget(settings.getDELA_TRANSFER_HTTP_ENDPOINT()) - .setPath("torrent/hops/download/advance/xml") - .setPayload(reqContent); - SuccessJSON result = rc.doPost(); - } catch (IllegalStateException ise) { - logger.log(Level.WARNING, "dela communication fail:{0}", ise.getMessage()); - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.DELA, null, ise.getMessage(), ise); - } - } - - public void cancel(String publicDSId) throws DelaException { - if(!delaStateController.transferDelaAvailable()) { - throw new DelaException(RESTCodes.DelaErrorCode.DELA_TRANSFER_NOT_AVAILABLE, Level.SEVERE, - DelaException.Source.LOCAL); - } - - try { - ClientWrapper rc = ClientWrapper - .httpInstance(SuccessJSON.class) - .setTarget(settings.getDELA_TRANSFER_HTTP_ENDPOINT()) - .setPath("torrent/hops/stop") - .setPayload(new TorrentId(publicDSId)); - SuccessJSON result = rc.doPost(); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.DELA, null, ise.getMessage(), ise); - } - } - - public HopsContentsSummaryJSON.Contents getContents(List projectIds) throws DelaException { - if(!delaStateController.transferDelaAvailable()) { - throw new DelaException(RESTCodes.DelaErrorCode.DELA_TRANSFER_NOT_AVAILABLE, Level.WARNING, - DelaException.Source.LOCAL); - } - HopsContentsReqJSON reqContent = new HopsContentsReqJSON(projectIds); - try { - ClientWrapper rc = ClientWrapper - .httpInstance(HopsContentsSummaryJSON.JsonWrapper.class) - .setTarget(settings.getDELA_TRANSFER_HTTP_ENDPOINT()) - .setPath("library/hopscontents") - .setPayload(reqContent); - HopsContentsSummaryJSON.Contents result = rc.doPost().resolve(); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.DELA, null, ise.getMessage(), ise); - } - } - - public TorrentExtendedStatusJSON details(TorrentId torrentId) throws DelaException { - if(!delaStateController.transferDelaAvailable()) { - throw new DelaException(RESTCodes.DelaErrorCode.DELA_TRANSFER_NOT_AVAILABLE, Level.SEVERE, - DelaException.Source.LOCAL); - } - try { - ClientWrapper rc = ClientWrapper - .httpInstance(TorrentExtendedStatusJSON.class) - .setTarget(settings.getDELA_TRANSFER_HTTP_ENDPOINT()) - .setPath("/library/extended") - .setPayload(torrentId); - TorrentExtendedStatusJSON result = rc.doPost(); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.DELA, null, ise.getMessage(), ise); - } - } - - /** - * @return - */ - public Pair, List> getContents() throws DelaException { - if(!delaStateController.transferDelaAvailable()) { - throw new DelaException(RESTCodes.DelaErrorCode.DELA_TRANSFER_NOT_AVAILABLE, Level.SEVERE, - DelaException.Source.LOCAL); - } - HopsContentsSummaryJSON.Contents contents = TransferDelaController.this.getContents(new LinkedList<>()); - List upldDSIds = new LinkedList<>(); - List dwnlDSIds = new LinkedList<>(); - for (ElementSummaryJSON[] ea : contents.getContents().values()) { - for (ElementSummaryJSON e : ea) { - if (e.getTorrentStatus().toLowerCase().equals("uploading")) { - upldDSIds.add(e.getTorrentId().getVal()); - } else if (e.getTorrentStatus().toLowerCase().equals("downloading")) { - dwnlDSIds.add(e.getTorrentId().getVal()); - } - } - } - return Pair.with(upldDSIds, dwnlDSIds); - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/cluster/ClusterDatasetController.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/cluster/ClusterDatasetController.java deleted file mode 100644 index bf9c4a4cda..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/cluster/ClusterDatasetController.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.cluster; - -import io.hops.hopsworks.common.dao.dataset.DatasetFacade; -import io.hops.hopsworks.persistence.entity.dataset.Dataset; -import io.hops.hopsworks.persistence.entity.dataset.SharedState; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import java.util.List; -import java.util.logging.Logger; - -@Stateless -@TransactionAttribute(TransactionAttributeType.NEVER) -public class ClusterDatasetController { - - private Logger logger = Logger.getLogger(ClusterDatasetController.class.getName()); - - @EJB - private DatasetFacade datasetFacade; - - public List getPublicDatasets() { - return datasetFacade.findPublicDatasetsByState(SharedState.CLUSTER.state); - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/common/ClusterAddressDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/common/ClusterAddressDTO.java deleted file mode 100644 index 08778538c7..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/common/ClusterAddressDTO.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.common; - -import java.io.Serializable; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class ClusterAddressDTO implements Serializable { - private String clusterId; - private String delaTransferAddress; - private String delaClusterAddress; - - public ClusterAddressDTO() { - } - - public ClusterAddressDTO(String clusterId, String delaTransferAddress, String delaClusterAddress) { - this.clusterId = clusterId; - this.delaTransferAddress = delaTransferAddress; - this.delaClusterAddress = delaClusterAddress; - } - - public String getClusterId() { - return clusterId; - } - - public void setClusterId(String clusterId) { - this.clusterId = clusterId; - } - - public String getDelaTransferAddress() { - return delaTransferAddress; - } - - public void setDelaTransferAddress(String delaTransferAddress) { - this.delaTransferAddress = delaTransferAddress; - } - - public String getDelaClusterAddress() { - return delaClusterAddress; - } - - public void setDelaClusterAddress(String delaClusterAddress) { - this.delaClusterAddress = delaClusterAddress; - } - - @Override - public String toString() { - return "CAdr{" + "cId=" + clusterId + ", dTAdr=" + delaTransferAddress + ", dCAdr=" + delaClusterAddress + '}'; - } -} \ No newline at end of file diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/common/UserDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/common/UserDTO.java deleted file mode 100644 index e815852850..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/common/UserDTO.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.common; - -import javax.xml.bind.annotation.XmlRootElement; - -public class UserDTO { - - @XmlRootElement - public static class Publish { - - private String firstname; - private String lastname; - private String email; - - public Publish() { - } - - public Publish(String firstname, String lastname, String email) { - this.firstname = firstname; - this.lastname = lastname; - this.email = email; - } - - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - public String getLastname() { - return lastname; - } - - public void setLastname(String lastname) { - this.lastname = lastname; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - } - - @XmlRootElement - public static class Complete { - private Integer userId; - private String firstname; - private String lastname; - private String email; - private String organization; - - public Complete() { - } - - public Integer getUserId() { - return userId; - } - - public void setUserId(Integer userId) { - this.userId = userId; - } - - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - public String getLastname() { - return lastname; - } - - public void setLastname(String lastname) { - this.lastname = lastname; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getOrganization() { - return organization; - } - - public void setOrganization(String organization) { - this.organization = organization; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/ClusterServiceDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/ClusterServiceDTO.java deleted file mode 100644 index 82d09de455..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/ClusterServiceDTO.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopssite; - -import java.util.List; -import javax.xml.bind.annotation.XmlRootElement; - -public class ClusterServiceDTO { - @XmlRootElement - public static class Register { - - private String delaTransferAddress; - private String delaClusterAddress; - - public Register() { - } - - public Register(String delaTransferAddress, String delaClusterAddress) { - this.delaTransferAddress = delaTransferAddress; - this.delaClusterAddress = delaClusterAddress; - } - - public String getDelaTransferAddress() { - return delaTransferAddress; - } - - public void setDelaTransferAddress(String delaTransferAddress) { - this.delaTransferAddress = delaTransferAddress; - } - - public String getDelaClusterAddress() { - return delaClusterAddress; - } - - public void setDelaClusterAddress(String delaClusterAddress) { - this.delaClusterAddress = delaClusterAddress; - } - } - - @XmlRootElement - public static class HeavyPing { - - private List upldDSIds; - private List dwnlDSIds; - - public HeavyPing() { - } - - public HeavyPing(List upldDSIds, List dwnlDSIds) { - this.upldDSIds = upldDSIds; - this.dwnlDSIds = dwnlDSIds; - } - - public List getUpldDSIds() { - return upldDSIds; - } - - public void setUpldDSIds(List upldDSIds) { - this.upldDSIds = upldDSIds; - } - - public List getDwnlDSIds() { - return dwnlDSIds; - } - - public void setDwnlDSIds(List dwnlDSIds) { - this.dwnlDSIds = dwnlDSIds; - } - } - - @XmlRootElement - public static class Ping { - private int upldDSSize; - private int dwnlDSSize; - - public Ping() { - } - - public Ping(int upldDSSize, int dwnlDSSize) { - this.upldDSSize = upldDSSize; - this.dwnlDSSize = dwnlDSSize; - } - - public int getUpldDSSize() { - return upldDSSize; - } - - public void setUpldDSSize(int upldDSSize) { - this.upldDSSize = upldDSSize; - } - - public int getDwnlDSSize() { - return dwnlDSSize; - } - - public void setDwnlDSSize(int dwnlDSSize) { - this.dwnlDSSize = dwnlDSSize; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/CommentDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/CommentDTO.java deleted file mode 100644 index 3137c9e9c1..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/CommentDTO.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopssite; - -import io.hops.hopsworks.dela.dto.common.UserDTO; -import java.util.Date; -import javax.xml.bind.annotation.XmlRootElement; - -public class CommentDTO { - - @XmlRootElement - public static class Publish { - - private String content; - private String userEmail; - - public Publish() { - } - - public Publish(String userEmail, String content) { - this.content = content; - this.userEmail = userEmail; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getUserEmail() { - return userEmail; - } - - public void setUserEmail(String userEmail) { - this.userEmail = userEmail; - } - } - - @XmlRootElement - public static class RetrieveComment { - - private Integer id; - private String content; - private UserDTO.Complete user; - private Date datePublished; - - public RetrieveComment() { - } - - public RetrieveComment(Integer id, String content, UserDTO.Complete user, Date datePublished) { - this.id = id; - this.content = content; - this.user = user; - this.datePublished = datePublished; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public UserDTO.Complete getUser() { - return user; - } - - public void setUser(UserDTO.Complete user) { - this.user = user; - } - - public Date getDatePublished() { - return datePublished; - } - - public void setDatePublished(Date datePublished) { - this.datePublished = datePublished; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/CommentIssueDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/CommentIssueDTO.java deleted file mode 100644 index c1033d271f..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/CommentIssueDTO.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopssite; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class CommentIssueDTO { - - private String type; - private String msg; - private String userEmail; - - public CommentIssueDTO() { - } - - public CommentIssueDTO(String type, String msg, String userEmail) { - this.type = type; - this.msg = msg; - this.userEmail = userEmail; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - - public String getUserEmail() { - return userEmail; - } - - public void setUserEmail(String userEmail) { - this.userEmail = userEmail; - } -} \ No newline at end of file diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/DatasetDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/DatasetDTO.java deleted file mode 100644 index 27df6617e0..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/DatasetDTO.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopssite; - -import java.io.Serializable; -import java.util.Collection; -import java.util.Date; -import javax.xml.bind.annotation.XmlRootElement; - -public class DatasetDTO implements Serializable { - - @XmlRootElement - public static class Proto { - private String name; - private String description; - private Collection categories; - private long size; - private String userEmail; - - public Proto() { - } - - public Proto(String name, String description, Collection categories, long size, - String userEmail) { - this.name = name; - this.description = description; - this.categories = categories; - this.userEmail = userEmail; - this.size = size; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public Collection getCategories() { - return categories; - } - - public void setCategories(Collection categories) { - this.categories = categories; - } - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - - public String getUserEmail() { - return userEmail; - } - - public void setUserEmail(String userEmail) { - this.userEmail = userEmail; - } - } - - @XmlRootElement - public static class Search implements Serializable { - - private String name; - private int version; - private String description; - - public Search() { - } - - public Search(String name, int version, String description) { - this.name = name; - this.version = version; - this.description = description; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getVersion() { - return version; - } - - public void setVersion(int version) { - this.version = version; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - } - - @XmlRootElement - public static class Details implements Serializable { - - private Owner owner; - private Collection categories; - private Date publishedOn; - private long size; - private Health datasetHealth; - - public Details() { - } - - public Details(Owner owner, Collection categories, Date publishedOn, long size, Health datasetHealth) { - this.owner = owner; - this.categories = categories; - this.publishedOn = publishedOn; - this.size = size; - this.datasetHealth = datasetHealth; - } - - public Owner getOwner() { - return owner; - } - - public void setOwner(Owner owner) { - this.owner = owner; - } - - public Collection getCategories() { - return categories; - } - - public void setCategories(Collection categories) { - this.categories = categories; - } - - public Date getPublishedOn() { - return publishedOn; - } - - public void setPublishedOn(Date publishedOn) { - this.publishedOn = publishedOn; - } - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - - public Health getDatasetHealth() { - return datasetHealth; - } - - public void setDatasetHealth(Health datasetHealth) { - this.datasetHealth = datasetHealth; - } - } - - @XmlRootElement - public static class Health implements Serializable { - - private int seeders; - private int leechers; - - public Health() { - } - - public Health(int seeders, int leechers) { - this.seeders = seeders; - this.leechers = leechers; - } - - public int getSeeders() { - return seeders; - } - - public void setSeeders(int seeders) { - this.seeders = seeders; - } - - public int getLeechers() { - return leechers; - } - - public void setLeechers(int leechers) { - this.leechers = leechers; - } - } - - @XmlRootElement - public static class Complete implements Serializable { - - private Owner owner; - private String name; - private String description; - private Collection categories; - private Date publishedOn; - private int rating; - private long size; - - public Complete() { - } - - public Complete(Owner owner, String name, String description, Collection categories, Date publishedOn, - int rating, long size) { - this.owner = owner; - this.name = name; - this.description = description; - this.categories = categories; - this.publishedOn = publishedOn; - this.rating = rating; - this.size = size; - } - - public Owner getOwner() { - return owner; - } - - public void setOwner(Owner owner) { - this.owner = owner; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public Collection getCategories() { - return categories; - } - - public void setCategories(Collection categories) { - this.categories = categories; - } - - public Date getPublishedOn() { - return publishedOn; - } - - public void setPublishedOn(Date publishedOn) { - this.publishedOn = publishedOn; - } - - public int getRating() { - return rating; - } - - public void setRating(int rating) { - this.rating = rating; - } - - public long getSize() { - return size; - } - - public void setSize(long size) { - this.size = size; - } - } - - @XmlRootElement - public static class Owner implements Serializable { - - private String clusterDescription; - private String userDescription; - - public Owner() { - } - - public Owner(String clusterDescription, String userDescription) { - this.clusterDescription = clusterDescription; - this.userDescription = userDescription; - } - - public String getClusterDescription() { - return clusterDescription; - } - - public void setClusterDescription(String clusterDescription) { - this.clusterDescription = clusterDescription; - } - - public String getUserDescription() { - return userDescription; - } - - public void setUserDescription(String userDescription) { - this.userDescription = userDescription; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/HopsSiteDatasetDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/HopsSiteDatasetDTO.java deleted file mode 100644 index cce89deaf2..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/HopsSiteDatasetDTO.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopssite; - -import java.util.Date; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class HopsSiteDatasetDTO { - - private String publicId; - private String name; - private int version; - private String description; - private Date madePublicOn; - private long dsSize; - private Integer rating; - private boolean localDataset; - - public HopsSiteDatasetDTO() { - } - - public HopsSiteDatasetDTO(String publicId, String name, int version, String description, Date madePublicOn, - long dsSize, Integer rating) { - this.publicId = publicId; - this.name = name; - this.version = version; - this.description = description; - this.madePublicOn = madePublicOn; - this.dsSize = dsSize; - this.rating = rating; - } - - public String getPublicId() { - return publicId; - } - - public void setPublicId(String publicId) { - this.publicId = publicId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getVersion() { - return version; - } - - public void setVersion(int version) { - this.version = version; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public Date getMadePublicOn() { - return madePublicOn; - } - - public void setMadePublicOn(Date madePublicOn) { - this.madePublicOn = madePublicOn; - } - - public long getDsSize() { - return dsSize; - } - - public void setDsSize(long dsSize) { - this.dsSize = dsSize; - } - - public Integer getRating() { - return rating; - } - - public void setRating(Integer rating) { - this.rating = rating; - } - - public boolean isLocalDataset() { - return localDataset; - } - - public void setLocalDataset(boolean localDataset) { - this.localDataset = localDataset; - } - - @Override - public String toString() { - return "HopsSiteDatasetDTO{" + "publicId=" + publicId + ", name=" + name + ", dsSize=" + dsSize + ", rating=" - + rating + '}'; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/RateDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/RateDTO.java deleted file mode 100644 index 5b123abb3b..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/RateDTO.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopssite; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class RateDTO { - - private int rating; - private String userEmail; - - public RateDTO() { - } - - public RateDTO(String userEmail, int rating) { - this.rating = rating; - this.userEmail = userEmail; - } - - public int getRating() { - return rating; - } - - public void setRating(int rating) { - this.rating = rating; - } - - public String getUserEmail() { - return userEmail; - } - - public void setUserEmail(String userEmail) { - this.userEmail = userEmail; - } -} \ No newline at end of file diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/RatingDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/RatingDTO.java deleted file mode 100644 index 353df3466c..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/RatingDTO.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopssite; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class RatingDTO { - - private int rate; - private int ratedBy; - - public RatingDTO() { - } - - public RatingDTO(int rate, int ratedBy) { - this.rate = rate; - this.ratedBy = ratedBy; - } - - public int getRate() { - return rate; - } - - public void setRate(int rate) { - this.rate = rate; - } - - public int getRatedBy() { - return ratedBy; - } - - public void setRatedBy(int ratedBy) { - this.ratedBy = ratedBy; - } -} \ No newline at end of file diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/SearchServiceDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/SearchServiceDTO.java deleted file mode 100644 index 0255e2f129..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopssite/SearchServiceDTO.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopssite; - -import io.hops.hopsworks.dela.dto.common.ClusterAddressDTO; -import java.io.Serializable; -import java.util.List; -import javax.xml.bind.annotation.XmlRootElement; - -public class SearchServiceDTO { - @XmlRootElement - public static class Params implements Serializable { - - private String searchTerm; - - public Params() { - } - - public Params(String searchTerm) { - this.searchTerm = searchTerm; - } - - public String getSearchTerm() { - return searchTerm; - } - - public void setSearchTerm(String searchTerm) { - this.searchTerm = searchTerm; - } - } - - @XmlRootElement - public static class SearchResult implements Serializable { - - private String sessionId; - private int nrHits; - - public SearchResult() { - } - - public SearchResult(String sessionId, int nrHits) { - this.sessionId = sessionId; - this.nrHits = nrHits; - } - - public String getSessionId() { - return sessionId; - } - - public void setSessionId(String sessionId) { - this.sessionId = sessionId; - } - - public int getNrHits() { - return nrHits; - } - - public void setNrHits(int nrHits) { - this.nrHits = nrHits; - } - } - - @XmlRootElement - public static class Item implements Serializable { - - private String publicDSId; - private DatasetDTO.Search dataset; - private float score; - - public Item() { - } - - public Item(String publicDSId, DatasetDTO.Search dataset, float score) { - this.publicDSId = publicDSId; - this.dataset = dataset; - this.score = score; - } - - public String getPublicDSId() { - return publicDSId; - } - - public void setPublicDSId(String publicDSId) { - this.publicDSId = publicDSId; - } - - public DatasetDTO.Search getDataset() { - return dataset; - } - - public void setDataset(DatasetDTO.Search dataset) { - this.dataset = dataset; - } - - public float getScore() { - return score; - } - - public void setScore(float score) { - this.score = score; - } - } - - public static class ItemDetails implements Serializable { - private DatasetDTO.Details dataset; - private List bootstrap; - - public ItemDetails() { - } - - public ItemDetails(DatasetDTO.Details dataset, List bootstrap) { - this.dataset = dataset; - this.bootstrap = bootstrap; - } - - public DatasetDTO.Details getDataset() { - return dataset; - } - - public void setDataset(DatasetDTO.Details dataset) { - this.dataset = dataset; - } - - public List getBootstrap() { - return bootstrap; - } - - public void setBootstrap(List bootstrap) { - this.bootstrap = bootstrap; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopsworks/HopsworksSearchDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopsworks/HopsworksSearchDTO.java deleted file mode 100644 index 44d7d3aada..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopsworks/HopsworksSearchDTO.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopsworks; - -import io.hops.hopsworks.dela.dto.hopssite.SearchServiceDTO; -import java.io.Serializable; -import javax.xml.bind.annotation.XmlRootElement; - -public class HopsworksSearchDTO { - @XmlRootElement - public static class Item implements Serializable { - - private String name; - private int version; - private String description; - private String publicId; - private String type; - private boolean public_ds; - private boolean localDataset; - private boolean downloading; - private float score; - - public Item() { - } - - public Item(SearchServiceDTO.Item item) { - this.type = "ds"; - this.public_ds = true; - this.localDataset = false; - this.downloading = false; - - this.publicId = item.getPublicDSId(); - this.name = item.getDataset().getName(); - this.version = item.getDataset().getVersion(); - this.description = item.getDataset().getDescription(); - this.score = item.getScore(); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getVersion() { - return version; - } - - public void setVersion(int version) { - this.version = version; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public boolean getPublic_ds() { - return public_ds; - } - - public void setPublic_ds(boolean public_ds) { - this.public_ds = public_ds; - } - - public boolean isLocalDataset() { - return localDataset; - } - - public void setLocalDataset(boolean localDataset) { - this.localDataset = localDataset; - } - - public boolean isDownloading() { - return downloading; - } - - public void setDownloading(boolean downloading) { - this.downloading = downloading; - } - - public String getPublicId() { - return publicId; - } - - public void setPublicId(String publicId) { - this.publicId = publicId; - } - - public boolean isPublic_ds() { - return public_ds; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopsworks/HopsworksTransferDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopsworks/HopsworksTransferDTO.java deleted file mode 100644 index d17672521c..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/dto/hopsworks/HopsworksTransferDTO.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.dto.hopsworks; - -import io.hops.hopsworks.dela.dto.common.ClusterAddressDTO; -import java.util.List; -import javax.xml.bind.annotation.XmlRootElement; - -public class HopsworksTransferDTO { - - @XmlRootElement - public static class Download { - - private int projectId; - - private String publicDSId; - private String name; - private List bootstrap; - - private String topics; - - public Download() { - } - - public int getProjectId() { - return projectId; - } - - public void setProjectId(int projectId) { - this.projectId = projectId; - } - - public String getPublicDSId() { - return publicDSId; - } - - public void setPublicDSId(String publicDSId) { - this.publicDSId = publicDSId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public List getBootstrap() { - return bootstrap; - } - - public void setBootstrap(List bootstrap) { - this.bootstrap = bootstrap; - } - - public String getTopics() { - return topics; - } - - public void setTopics(String topics) { - this.topics = topics; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopsSite.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopsSite.java deleted file mode 100644 index 00901baa01..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopsSite.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.hopssite; - -import io.hops.hopsworks.exceptions.DelaException; - -public class HopsSite { - - public static class ClusterService { - - public static String delaVersion() { - return "public/cluster/dela/version"; - } - - public static String registerCluster() { - return "private/cluster/register"; - } - - public static String heavyPing(String publicCId) { - return "private/cluster/heavyPing/" + publicCId; - } - - public static String ping(String publicCId) { - return "private/cluster/ping/" + publicCId; - } - } - - public static class UserService { - public static String user() { - return "private/user"; - } - - public static String registerUser(String publicCId) { - return "private/user/register/" + publicCId; - } - - public static String getUser(String publicCId, String email) { - return "private/user/" + publicCId + "/" + email; - } - } - - public static class DatasetService { - - public static String dataset() { - return "private/dataset"; - } - - public static String datasetByPublicId() { - return "private/dataset/byPublicId"; - } - - public static String datasetIssue() { - return "private/dataset/issue"; - } - - public static String datasetCategory() { - return "private/dataset/category"; - } - - public static String datasetPopular() { - return "private/dataset/popular"; - } - - public static String publish(String publicCId) { - return "private/dataset/publish/" + publicCId; - } - - public static String unpublish(String publicCId, String publicDSId) { - return "private/dataset/unpublish/" + publicCId + "/" + publicDSId; - } - - public static String download(String publicCId, String publicDSId) { - return "private/dataset/download/" + publicCId + "/" + publicDSId; - } - - public static String complete(String publicCId, String publicDSId) { - return "private/dataset/complete/" + publicCId + "/" + publicDSId; - } - - public static String remove(String publicCId, String publicDSId) { - return "private/dataset/remove/" + publicCId + "/" + publicDSId; - } - - public static String search() { - return "public/dataset/search"; - } - - public static String searchPage(String sessionId, Integer startItem, Integer nrItems) { - return "public/dataset/search/" + sessionId + "/page/" + startItem + "/" + nrItems; - } - - public static String details(String publicDSId) { - return "public/dataset/" + publicDSId + "/details"; - } - } - - public static class RatingService { - - public static String rating() { - return "private/rating"; - } - - public static String getDatasetAllByPublicId() { - return "private/rating/all/byPublicId"; - } - public static String getDatasetAllRating(String publicDSId) { - return "private/rating/dataset/" + publicDSId + "/all"; - } - - public static String getDatasetUserRating(String publicCId, String publicDSId) { - return "private/rating/cluster/" + publicCId + "/dataset/" + publicDSId + "/user"; - } - - public static String addRating(String publicCId, String publicDSId) { - return "private/rating/cluster/" + publicCId + "/dataset/" + publicDSId + "/add"; - } - } - - public static class CommentService { - public static String getDatasetAllComments(String publicDSId) { - return "private/comment/dataset/" + publicDSId + "/all"; - } - - public static String addComment(String publicCId, String publicDSId) { - return "private/comment/cluster/" + publicCId + "/dataset/" + publicDSId + "/add"; - } - - public static String updateComment(String publicCId, String publicDSId, Integer commentId) { - return "private/comment/cluster/" + publicCId + "/dataset/" + publicDSId + "/update/" + commentId; - } - - public static String removeComment(String publicCId, String publicDSId, Integer commentId) { - return "private/comment/cluster/" + publicCId + "/dataset/" + publicDSId + "/delete/" + commentId; - } - - public static String reportComment(String publicCId, String publicDSId, Integer commentId) { - return "private/comment/cluster/" + publicCId + "/dataset/" + publicDSId + "/report/" + commentId; - } - } - - public static interface UserFunc { - - public C perform() throws DelaException; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopsSiteExceptions.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopsSiteExceptions.java deleted file mode 100644 index c7e323cec9..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopsSiteExceptions.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.hopssite; - -public enum HopsSiteExceptions { - ALREADY_REGISTERED; - - public boolean is(String msg) { - if(this.name().toLowerCase().equals(msg.toLowerCase())) { - return true; - } - return false; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopssiteController.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopssiteController.java deleted file mode 100644 index 8a1ecfa95e..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/hopssite/HopssiteController.java +++ /dev/null @@ -1,650 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.hopssite; - -import com.google.gson.Gson; -import io.hops.hopsworks.common.util.ClientWrapper; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.dela.DelaStateController; -import io.hops.hopsworks.dela.dto.common.UserDTO; -import io.hops.hopsworks.dela.dto.hopssite.ClusterServiceDTO; -import io.hops.hopsworks.dela.dto.hopssite.CommentDTO; -import io.hops.hopsworks.dela.dto.hopssite.CommentIssueDTO; -import io.hops.hopsworks.dela.dto.hopssite.DatasetDTO; -import io.hops.hopsworks.dela.dto.hopssite.HopsSiteDatasetDTO; -import io.hops.hopsworks.dela.dto.hopssite.RateDTO; -import io.hops.hopsworks.dela.dto.hopssite.RatingDTO; -import io.hops.hopsworks.dela.dto.hopssite.SearchServiceDTO; -import io.hops.hopsworks.dela.old_hopssite_dto.DatasetIssueDTO; -import io.hops.hopsworks.dela.old_hopssite_dto.PopularDatasetJSON; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.restutils.RESTCodes; -import io.hops.hopsworks.util.SettingsHelper; - -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ejb.TransactionAttribute; -import javax.ejb.TransactionAttributeType; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLSession; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -@Stateless -@TransactionAttribute(TransactionAttributeType.NEVER) -public class HopssiteController { - - private static final Logger LOG = Logger.getLogger(HopssiteController.class.getName()); - - @EJB - private Settings settings; - @EJB - private DelaStateController delaStateCtrl; - - private void checkSetupReady() throws DelaException { - delaStateCtrl.checkHopsworksDelaSetup(); - } - - private void checkHopssiteReady() throws DelaException { - checkSetupReady(); - delaStateCtrl.checkHopssiteAvailable(); - } - //******************************************************************************************************************** - - private ClientWrapper getClient(String path, Class resultClass) { - String hopsSite = settings.getHOPSSITE(); - return ClientWrapper.httpsInstance(delaStateCtrl.getKeystore(), delaStateCtrl.getTruststore(), - delaStateCtrl.getKeystorePassword(), new HopsSiteHostnameVerifier(settings), resultClass) - .setTarget(hopsSite).setPath(path); - } - - //*************************************************HEARTBEAT********************************************************** - public String delaVersion() throws DelaException { - checkSetupReady(); - try { - ClientWrapper client = getClient(HopsSite.ClusterService.delaVersion(), String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:cluster - {0}", client.getFullPath()); - String result = (String) client.doGet(); - LOG.log(Settings.DELA_DEBUG, "hops-site:cluster - done {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public String registerCluster(String delaClusterAddress, String delaTransferAddress) - throws DelaException { - checkHopssiteReady(); - try { - ClusterServiceDTO.Register req = new ClusterServiceDTO.Register(delaTransferAddress, delaClusterAddress); - ClientWrapper client = getClient(HopsSite.ClusterService.registerCluster(), String.class); - client.setPayload(req); - LOG.log(Settings.DELA_DEBUG, "hops-site:cluster - {0}", client.getFullPath()); - String result = (String) client.doPut(); - LOG.log(Settings.DELA_DEBUG, "hops-site:cluster - done {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, - Level.SEVERE, DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public void heavyPing(List upldDSIds, List dwnlDSIds) throws DelaException { - checkHopssiteReady(); - String publicCId = SettingsHelper.clusterId(settings); - try { - ClientWrapper client = getClient(HopsSite.ClusterService.heavyPing(publicCId), String.class); - ClusterServiceDTO.HeavyPing req = new ClusterServiceDTO.HeavyPing(upldDSIds, dwnlDSIds); - client.setPayload(req); - LOG.log(Settings.DELA_DEBUG, "hops-site:cluster - {0}", client.getFullPath()); - String result = (String) client.doPut(); - LOG.log(Settings.DELA_DEBUG, "hops-site:cluster - done {0}", client.getFullPath()); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public void ping(ClusterServiceDTO.Ping ping) throws DelaException { - checkHopssiteReady(); - String publicCId = SettingsHelper.clusterId(settings); - try { - ClientWrapper client = getClient(HopsSite.ClusterService.ping(publicCId), String.class); - client.setPayload(ping); - LOG.log(Settings.DELA_DEBUG, "hops-site:cluster - {0}", client.getFullPath()); - String result = (String) client.doPut(); - LOG.log(Settings.DELA_DEBUG, "hops-site:cluster -done {0}", client.getFullPath()); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - //*****************************************************USER*********************************************************** - public int registerUser(String publicCId, String firstname, String lastname, String userEmail) - throws DelaException { - checkHopssiteReady(); - try { - UserDTO.Publish user = new UserDTO.Publish(firstname, lastname, userEmail); - ClientWrapper client = getClient(HopsSite.UserService.registerUser(publicCId), String.class); - client.setPayload(user); - LOG.log(Settings.DELA_DEBUG, "hops-site:user - {0}", client.getFullPath()); - Integer result = Integer.parseInt((String) client.doPost()); - LOG.log(Settings.DELA_DEBUG, "hops-site:user - done {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public UserDTO.Complete getUser(String email) throws DelaException { - checkHopssiteReady(); - String publicCId = SettingsHelper.clusterId(settings); - try { - ClientWrapper client = getClient(HopsSite.UserService.getUser(publicCId, email), UserDTO.Complete.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:user - {0}", client.getFullPath()); - UserDTO.Complete result = (UserDTO.Complete) client.doGet(); - LOG.log(Settings.DELA_DEBUG, "hops-site:user - done {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public Integer getUserId(String email) throws DelaException { - checkHopssiteReady(); - String publicCId = SettingsHelper.clusterId(settings); - try { - ClientWrapper client = getClient(HopsSite.UserService.getUser(publicCId, email), UserDTO.Complete.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:user - {0}", client.getFullPath()); - UserDTO.Complete result = (UserDTO.Complete) client.doGet(); - LOG.log(Settings.DELA_DEBUG, "hops-site:user - done {0}", client.getFullPath()); - return result.getUserId(); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public C performAsUser(Users user, HopsSite.UserFunc func) throws DelaException { - checkHopssiteReady(); - C result; - String publicCId = SettingsHelper.clusterId(settings); - try { - result = func.perform(); - } catch (DelaException tpe) { - if (RESTCodes.DelaErrorCode.USER_NOT_REGISTERED.getMessage().equals(tpe.getMessage())) { - registerUser(publicCId, user.getFname(), user.getLname(), user.getEmail()); - result = func.perform(); - } else { - throw tpe; - } - } catch (IllegalStateException ise) { - if (RESTCodes.DelaErrorCode.USER_NOT_REGISTERED.getMessage().equals(ise.getMessage())) { - registerUser(publicCId, user.getFname(), user.getLname(), user.getEmail()); - result = func.perform(); - } else { - throw ise; - } - } - return result; - } - - //****************************************************TRACKER******************************************************** - public String publish(String datasetName, String description, Collection categories, - long size, String userEmail) throws DelaException { - checkHopssiteReady(); - String publicCId = SettingsHelper.clusterId(settings); - try { - DatasetDTO.Proto msg = new DatasetDTO.Proto(datasetName, description, categories, size, userEmail); - ClientWrapper client = getClient(HopsSite.DatasetService.publish(publicCId), String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - {0}", client.getFullPath()); - client.setPayload(msg); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - done {0}", client.getFullPath()); - String result = (String) client.doPost(); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public void download(String publicDSId) throws DelaException { - checkHopssiteReady(); - String publicCId = SettingsHelper.clusterId(settings); - - try { - ClientWrapper client = getClient(HopsSite.DatasetService.download(publicCId, publicDSId), String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - {0}", client.getFullPath()); - client.doPost(); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - done {0}", client.getFullPath()); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public void complete(String publicDSId) throws DelaException { - checkHopssiteReady(); - - String publicCId = SettingsHelper.clusterId(settings); - - try { - ClientWrapper client = getClient(HopsSite.DatasetService.complete(publicCId, publicDSId), String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - {0}", client.getFullPath()); - client.doPost(); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - done {0}", client.getFullPath()); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public void cancel(String publicDSId) throws DelaException { - checkHopssiteReady(); - - String publicCId = SettingsHelper.clusterId(settings); - - try { - ClientWrapper client = getClient(HopsSite.DatasetService.remove(publicCId, publicDSId), String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - {0}", client.getFullPath()); - client.doPost(); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - done {0}", client.getFullPath()); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - //*****************************************************SEARCH********************************************************* - public SearchServiceDTO.SearchResult search(String searchTerm) throws DelaException { - checkHopssiteReady(); - try { - SearchServiceDTO.Params req = new SearchServiceDTO.Params(searchTerm); - ClientWrapper client = getClient(HopsSite.DatasetService.search(), SearchServiceDTO.SearchResult.class); - client.setPayload(req); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - {0}", client.getFullPath()); - SearchServiceDTO.SearchResult result = (SearchServiceDTO.SearchResult) client.doPost(); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset:done - {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public SearchServiceDTO.Item[] page(String sessionId, int startItem, int nrItems) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.DatasetService.searchPage(sessionId, startItem, nrItems), - String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - {0}", client.getFullPath()); - String auxResult = (String) client.doGet(); - SearchServiceDTO.Item[] result = new Gson().fromJson(auxResult, SearchServiceDTO.Item[].class); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset:done - {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public SearchServiceDTO.ItemDetails details(String publicDSId) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.DatasetService.details(publicDSId), String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - {0}", client.getFullPath()); - String auxResult = (String) client.doGet(); - SearchServiceDTO.ItemDetails result = new Gson().fromJson(auxResult, SearchServiceDTO.ItemDetails.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset:done - {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - //*************************************************SETTINGS CHECK***************************************************** - public boolean updateUser(UserDTO.Publish userDTO) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.UserService.user(), String.class); - client.setPayload(userDTO); - String res = (String) client.doPut(); - return "OK".equals(res); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public boolean deleteUser(Integer uId) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.UserService.user() + "/" + uId, String.class); - String res = (String) client.doDelete(); - return "OK".equals(res); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - //***********************************************COMMENT PUBLIC******************************************************* - public List getDatasetAllComments(String publicDSId) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.CommentService.getDatasetAllComments(publicDSId), String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:get:all {0}", client.getFullPath()); - String aux = (String) client.doGet(); - CommentDTO.RetrieveComment[] result = new Gson().fromJson(aux, CommentDTO.RetrieveComment[].class); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:get:all - done {0}", client.getFullPath()); - return Arrays.asList(result); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public void addComment(String publicCId, String publicDSId, CommentDTO.Publish comment) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.CommentService.addComment(publicCId, publicDSId), String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:add {0}", client.getFullPath()); - client.setPayload(comment); - client.doPost(); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:add - done {0}", client.getFullPath()); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, - Level.SEVERE, DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public void updateComment(String publicCId, String publicDSId, Integer commentId, CommentDTO.Publish comment) - throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.CommentService.updateComment(publicCId, publicDSId, commentId), - String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:udpate {0}", client.getFullPath()); - client.setPayload(comment); - client.doPut(); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:udpate - done {0}", client.getFullPath()); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public void removeComment(String publicCId, String publicDSId, Integer commentId, String userEmail) - throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.CommentService.removeComment(publicCId, publicDSId, commentId), - String.class) - .setPayload(userEmail); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:remove {0}", client.getFullPath()); - client.doPost(); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:remove - done {0}", client.getFullPath()); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public void reportComment(String publicCId, String publicDSId, Integer commentId, CommentIssueDTO commentIssue) - throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.CommentService.reportComment(publicCId, publicDSId, commentId), - String.class); - client.setPayload(commentIssue); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:report {0}", client.getFullPath()); - client.doPost(); - LOG.log(Settings.DELA_DEBUG, "hops-site:comment:report - done {0}", client.getFullPath()); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - //**************************************************RATING PUBLIC***************************************************** - public RatingDTO getDatasetAllRating(String publicDSId) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.RatingService.getDatasetAllRating(publicDSId), RatingDTO.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:rating:get:all - {0}", client.getFullPath()); - RatingDTO result = (RatingDTO) client.doGet(); - LOG.log(Settings.DELA_DEBUG, "hops-site:rating:get:all - done {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - //**************************************************RATING CLUSTER**************************************************** - public RatingDTO getDatasetUserRating(String publicCId, String publicDSId, String email) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client - = getClient(HopsSite.RatingService.getDatasetUserRating(publicCId, publicDSId), RatingDTO.class) - .setPayload(email); - LOG.log(Settings.DELA_DEBUG, "hops-site:rating:get:user - {0}", client.getFullPath()); - RatingDTO result = (RatingDTO) client.doPost(); - LOG.log(Settings.DELA_DEBUG, "hops-site:rating:get:user - done {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public boolean addRating(String publicCId, String publicDSId, RateDTO datasetRating) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.RatingService.addRating(publicCId, publicDSId), String.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:rating:add - {0}", client.getFullPath()); - client.setPayload(datasetRating); - client.doPost(); - LOG.log(Settings.DELA_DEBUG, "hops-site:rating:add - done {0}", client.getFullPath()); - return true; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - //************************************************RATING FUTURE******************************************************* - public List getAllRatingsByPublicId(String publicId) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client - = getClient(HopsSite.RatingService.getDatasetAllByPublicId() + "/" + publicId, RateDTO.class); - return (List) client.doGetGenericType(); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public boolean updateRating(RateDTO datasetRating) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.RatingService.rating(), String.class); - client.setPayload(datasetRating); - String res = (String) client.doPut(); - return "OK".equals(res); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public boolean deleteRating(Integer ratingId) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.RatingService.rating() + "/" + ratingId, String.class); - String res = (String) client.doDelete(); - return "OK".equals(res); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - // dataset services - public List getAll() throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.DatasetService.dataset(), HopsSiteDatasetDTO.class); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - {0}", client.getFullPath()); - List result = (List) client.doGetGenericType(); - LOG.log(Settings.DELA_DEBUG, "hops-site:dataset - done {0}", client.getFullPath()); - return result; - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public DatasetDTO.Complete getDataset(String publicDSId) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.DatasetService.datasetByPublicId() + "/" + publicDSId, - DatasetDTO.Complete.class); - return (DatasetDTO.Complete) client.doGet(); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public SearchServiceDTO.ItemDetails getDatasetDetails(String publicDSId) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.DatasetService.dataset() + "/" + publicDSId + "/details", - SearchServiceDTO.ItemDetails.class); - return (SearchServiceDTO.ItemDetails) client.doGet(); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public boolean addDatasetIssue(DatasetIssueDTO datasetIssue) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.DatasetService.datasetIssue(), String.class); - client.setPayload(datasetIssue); - String res = (String) client.doPost(); - return "OK".equals(res); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public boolean addCategory(DatasetDTO dataset) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.DatasetService.datasetCategory(), String.class); - client.setPayload(dataset); - String res = (String) client.doPost(); - return "OK".equals(res); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public List getPopularDatasets() throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.DatasetService.datasetPopular(), PopularDatasetJSON.class); - return (List) client.doGetGenericType(); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public boolean addPopularDatasets(PopularDatasetJSON popularDatasetsJson) throws DelaException { - checkHopssiteReady(); - try { - ClientWrapper client = getClient(HopsSite.DatasetService.datasetPopular(), String.class); - client.setPayload(popularDatasetsJson); - String res = (String) client.doPost(); - return "OK".equals(res); - } catch (IllegalStateException ise) { - throw new DelaException(RESTCodes.DelaErrorCode.COMMUNICATION_FAILURE, Level.SEVERE, - DelaException.Source.HOPS_SITE, null, ise.getMessage(), ise); - } - } - - public String hopsSite() throws DelaException { - return SettingsHelper.hopsSite(settings); - } - - public static class HopsSiteHostnameVerifier implements HostnameVerifier { - - private final Settings settings; - - public HopsSiteHostnameVerifier(Settings settings) { - this.settings = settings; - } - - @Override - public boolean verify(String host, SSLSession ssls) { - String hopssite = settings.getHOPSSITE_HOST(); - if(hopssite != null) { - return hopssite.equals(host); - } - return true; //TODO Alex - should return false; but test it - we always set the hopssite though - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ElementSummaryJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ElementSummaryJSON.java deleted file mode 100644 index 4c10176ee7..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ElementSummaryJSON.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class ElementSummaryJSON { - - private String fileName; - private TorrentId torrentId; - private String torrentStatus; - - public ElementSummaryJSON(String name, TorrentId torrentId, String status) { - this.fileName = name; - this.torrentId = torrentId; - this.torrentStatus = status; - } - - public ElementSummaryJSON() { - } - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } - - public TorrentId getTorrentId() { - return torrentId; - } - - public void setTorrentId(TorrentId torrentId) { - this.torrentId = torrentId; - } - - public String getTorrentStatus() { - return torrentStatus; - } - - public void setTorrentStatus(String torrentStatus) { - this.torrentStatus = torrentStatus; - } - - @Override - public String toString() { - return "ElementSummaryJSON{" + "fileName=" + fileName + ", torrentId=" + torrentId + ", torrentStatus=" + - torrentStatus + '}'; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ErrorDescJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ErrorDescJSON.java deleted file mode 100644 index debe560a9a..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ErrorDescJSON.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class ErrorDescJSON { - - private String details; - - public ErrorDescJSON(String details) { - this.details = details; - } - - public ErrorDescJSON() { - } - - public String getDetails() { - return details; - } - - public void setDetails(String details) { - this.details = details; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ExtendedDetails.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ExtendedDetails.java deleted file mode 100644 index 2d2d414210..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ExtendedDetails.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import java.util.List; - -public class ExtendedDetails { - - private List hdfsDetails; - private List kafkaDetails; - - public ExtendedDetails() { - } - - public ExtendedDetails(List hdfsDetails, List kafkaDetails) { - this.hdfsDetails = hdfsDetails; - this.kafkaDetails = kafkaDetails; - } - - public List getHdfsDetails() { - return hdfsDetails; - } - - public void setHdfsDetails(List hdfsDetails) { - this.hdfsDetails = hdfsDetails; - } - - public List getKafkaDetails() { - return kafkaDetails; - } - - public void setKafkaDetails(List kafkaDetails) { - this.kafkaDetails = kafkaDetails; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/FileInfo.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/FileInfo.java deleted file mode 100644 index 10877efb31..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/FileInfo.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class FileInfo { - - private String fileName; - - private String schema; - - private long length; - - public FileInfo() { - } - - public FileInfo(String fileName, String schema, long length) { - this.fileName = fileName; - this.schema = schema; - } - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } - - public long getLength() { - return length; - } - - public void setLength(long length) { - this.length = length; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HDFSEndpoint.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HDFSEndpoint.java deleted file mode 100644 index 25fbd11ffc..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HDFSEndpoint.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class HDFSEndpoint { - - private String xmlPath; - private String user; - - public HDFSEndpoint() { - } - - public HDFSEndpoint(String xmlPath, String user) { - this.xmlPath = xmlPath; - this.user = user; - } - - public String getXmlPath() { - return xmlPath; - } - - public void setXmlPath(String xmlPath) { - this.xmlPath = xmlPath; - } - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - @Override - public String toString() { - return "HDFSEndpoint{" + "xmlPath=" + xmlPath + ", user=" + user + '}'; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HDFSResource.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HDFSResource.java deleted file mode 100644 index 5c8f2132a3..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HDFSResource.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class HDFSResource { - - private String dirPath; - private String fileName; - - public HDFSResource() { - } - - public HDFSResource(String dirPath, String fileName) { - this.dirPath = dirPath; - this.fileName = fileName; - } - - public String getDirPath() { - return dirPath; - } - - public void setDirPath(String dirPath) { - this.dirPath = dirPath; - } - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } - - @Override - public String toString() { - return "HDFSResource{" + "dirPath=" + dirPath + ", fileName=" + fileName + - '}'; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HdfsDetails.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HdfsDetails.java deleted file mode 100644 index 161b5f2fe1..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HdfsDetails.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class HdfsDetails { - private String file; - private HDFSResource resource; - - public HdfsDetails(String file, HDFSResource resource) { - this.file = file; - this.resource = resource; - } - - public HdfsDetails() { - } - - public String getFile() { - return file; - } - - public void setFile(String file) { - this.file = file; - } - - public HDFSResource getResource() { - return resource; - } - - public void setResource(HDFSResource resource) { - this.resource = resource; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsContentsReqJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsContentsReqJSON.java deleted file mode 100644 index 694f3cb033..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsContentsReqJSON.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import java.util.List; - -public class HopsContentsReqJSON { - - private List projectIds; - - public HopsContentsReqJSON() { - } - - public HopsContentsReqJSON(List projectIds) { - this.projectIds = projectIds; - } - - public List getProjectIds() { - return projectIds; - } - - public void setProjectIds(List projectId) { - this.projectIds = projectId; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsContentsSummaryJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsContentsSummaryJSON.java deleted file mode 100644 index e94337227f..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsContentsSummaryJSON.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import java.util.Map; -import java.util.TreeMap; -import javax.xml.bind.annotation.XmlRootElement; - -public class HopsContentsSummaryJSON { - - @XmlRootElement - public static class JsonWrapper { - - // - private ContentsElement[] contents = new ContentsElement[0]; - - public JsonWrapper() { - } - - public ContentsElement[] getContents() { - return contents; - } - - public void setContents(ContentsElement[] contents) { - if(contents == null) { - this.contents = new ContentsElement[0]; - return; - } - this.contents = contents; - } - - public Contents resolve() { - Map c = new TreeMap<>(); - for (ContentsElement ce : contents) { - c.put(ce.projectId, ce.projectContents); - } - return new Contents(c); - } - } - - @XmlRootElement - public static class ContentsElement { - - public Integer projectId; - public ElementSummaryJSON[] projectContents; - - private ContentsElement() { - } - - public ContentsElement(Integer projectId, ElementSummaryJSON[] projectContents) { - this.projectId = projectId; - this.projectContents = projectContents; - } - } - - @XmlRootElement - public static class Contents { - - private Map contents; - - public Contents(Map contents) { - this.contents = contents; - } - - public Contents() { - } - - public Map getContents() { - return contents; - } - - public void setContents(Map contents) { - this.contents = contents; - } - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsDatasetDetailsDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsDatasetDetailsDTO.java deleted file mode 100644 index 970942fd41..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsDatasetDetailsDTO.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class HopsDatasetDetailsDTO { - private String datasetName; - private Integer projectId; - private Integer datasetId; - - public HopsDatasetDetailsDTO() { - } - - public HopsDatasetDetailsDTO(String datasetName, Integer projectId, Integer datasetId) { - this.datasetName = datasetName; - this.projectId = projectId; - this.datasetId = datasetId; - } - - public String getDatasetName() { - return datasetName; - } - - public void setDatasetName(String datasetName) { - this.datasetName = datasetName; - } - - public Integer getProjectId() { - return projectId; - } - - public void setProjectId(Integer projectId) { - this.projectId = projectId; - } - - public Integer getDatasetId() { - return datasetId; - } - - public void setDatasetId(Integer datasetId) { - this.datasetId = datasetId; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsResource.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsResource.java deleted file mode 100644 index ece49f3354..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsResource.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -public class HopsResource { - - private final int projectId; - - public HopsResource(int projectId) { - this.projectId = projectId; - } - - public int getProjectId() { - return projectId; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentAdvanceDownload.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentAdvanceDownload.java deleted file mode 100644 index 40f1627a39..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentAdvanceDownload.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class HopsTorrentAdvanceDownload { - - private TorrentId torrentId; - private KafkaEndpoint kafkaEndpoint; - private HDFSEndpoint hdfsEndpoint; - private ExtendedDetails extendedDetails; - - public HopsTorrentAdvanceDownload() { - } - - public HopsTorrentAdvanceDownload(TorrentId torrentId, - KafkaEndpoint kafkaEndpoint, HDFSEndpoint hdfsEndpoint, - ExtendedDetails extendedDetails) { - this.torrentId = torrentId; - this.kafkaEndpoint = kafkaEndpoint; - this.hdfsEndpoint = hdfsEndpoint; - this.extendedDetails = extendedDetails; - } - - public TorrentId getTorrentId() { - return torrentId; - } - - public void setTorrentId(TorrentId torrentId) { - this.torrentId = torrentId; - } - - public KafkaEndpoint getKafkaEndpoint() { - return kafkaEndpoint; - } - - public void setKafkaEndpoint(KafkaEndpoint kafkaEndpoint) { - this.kafkaEndpoint = kafkaEndpoint; - } - - public ExtendedDetails getExtendedDetails() { - return extendedDetails; - } - - public void setExtendedDetails(ExtendedDetails extendedDetails) { - this.extendedDetails = extendedDetails; - } - - public HDFSEndpoint getHdfsEndpoint() { - return hdfsEndpoint; - } - - public void setHdfsEndpoint(HDFSEndpoint hdfsEndpoint) { - this.hdfsEndpoint = hdfsEndpoint; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentStartDownload.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentStartDownload.java deleted file mode 100644 index cf8cd9238c..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentStartDownload.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import io.hops.hopsworks.common.dela.AddressJSON; -import java.util.List; - -public class HopsTorrentStartDownload { - - private TorrentId torrentId; - private String torrentName; - private Integer projectId; - private Integer datasetId; - private HDFSResource manifestHDFSResource; - private List partners; - private HDFSEndpoint hdfsEndpoint; - - public HopsTorrentStartDownload(TorrentId torrentId, String torrentName, - Integer projectId, Integer datasetId, HDFSResource manifestHDFSResource, - List partners, HDFSEndpoint hdfsEndpoint) { - this.torrentId = torrentId; - this.torrentName = torrentName; - this.projectId = projectId; - this.datasetId = datasetId; - this.manifestHDFSResource = manifestHDFSResource; - this.partners = partners; - this.hdfsEndpoint = hdfsEndpoint; - } - - public HopsTorrentStartDownload() { - } - - public String getTorrentName() { - return torrentName; - } - - public void setTorrentName(String torrentName) { - this.torrentName = torrentName; - } - - public TorrentId getTorrentId() { - return torrentId; - } - - public void setTorrentId(TorrentId torrentId) { - this.torrentId = torrentId; - } - - public Integer getProjectId() { - return projectId; - } - - public void setProjectId(Integer projectId) { - this.projectId = projectId; - } - - public Integer getDatasetId() { - return datasetId; - } - - public void setDatasetId(Integer datasetId) { - this.datasetId = datasetId; - } - - public HDFSResource getManifestHDFSResource() { - return manifestHDFSResource; - } - - public void setManifestHDFSResource(HDFSResource manifestHDFSResource) { - this.manifestHDFSResource = manifestHDFSResource; - } - - public List getPartners() { - return partners; - } - - public void setPartners(List partners) { - this.partners = partners; - } - - public HDFSEndpoint getHdfsEndpoint() { - return hdfsEndpoint; - } - - public void setHdfsEndpoint(HDFSEndpoint hdfsEndpoint) { - this.hdfsEndpoint = hdfsEndpoint; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentUpload.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentUpload.java deleted file mode 100644 index 64da2eb892..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/HopsTorrentUpload.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class HopsTorrentUpload { - - private TorrentId torrentId; - private String torrentName; - private Integer projectId; - private Integer datasetId; - private HDFSResource manifestHDFSResource; - private HDFSEndpoint hdfsEndpoint; - - public HopsTorrentUpload(TorrentId torrentId, String torrentName, - Integer projectId, Integer datasetId, - HDFSResource manifestHDFSResource, - HDFSEndpoint hdfsEndpoint) { - this.torrentId = torrentId; - this.torrentName = torrentName; - this.projectId = projectId; - this.datasetId = datasetId; - this.manifestHDFSResource = manifestHDFSResource; - this.hdfsEndpoint = hdfsEndpoint; - } - - public HopsTorrentUpload() { - } - - public String getTorrentName() { - return torrentName; - } - - public void setTorrentName(String torrentName) { - this.torrentName = torrentName; - } - - public TorrentId getTorrentId() { - return torrentId; - } - - public void setTorrentId(TorrentId torrentId) { - this.torrentId = torrentId; - } - - public Integer getProjectId() { - return projectId; - } - - public void setProjectId(Integer projectId) { - this.projectId = projectId; - } - - public Integer getDatasetId() { - return datasetId; - } - - public void setDatasetId(Integer datasetId) { - this.datasetId = datasetId; - } - - public HDFSResource getManifestHDFSResource() { - return manifestHDFSResource; - } - - public void setManifestHDFSResource(HDFSResource manifestHDFSResource) { - this.manifestHDFSResource = manifestHDFSResource; - } - - public HDFSEndpoint getHdfsEndpoint() { - return hdfsEndpoint; - } - - public void setHdfsEndpoint(HDFSEndpoint hdfsEndpoint) { - this.hdfsEndpoint = hdfsEndpoint; - } - - @Override - public String toString() { - return "HopsTorrentUpload{" + "torrentId=" + torrentId + ", torrentName=" + - torrentName + ", manifestHDFSResource=" + manifestHDFSResource + - ", hdfsEndpoint=" + hdfsEndpoint + '}'; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaDetails.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaDetails.java deleted file mode 100644 index ff785f59d7..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaDetails.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class KafkaDetails { - - private String file; - private KafkaResource resource; - - public KafkaDetails(String file, KafkaResource resource) { - this.file = file; - this.resource = resource; - } - - public KafkaDetails() { - } - - public String getFile() { - return file; - } - - public void setFile(String file) { - this.file = file; - } - - public KafkaResource getResource() { - return resource; - } - - public void setResource(KafkaResource resource) { - this.resource = resource; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaEndpoint.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaEndpoint.java deleted file mode 100644 index ee8fb82085..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaEndpoint.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class KafkaEndpoint { - - private String brokerEndpoint; - private String restEndpoint; - private String domain; - private String projectId; - private String keyStore; - private String trustStore; - - public KafkaEndpoint() { - } - - public KafkaEndpoint(String brokerEndpoint, String restEndpoint, String domain, - String projectId, String keyStore, String trustStore) { - this.brokerEndpoint = brokerEndpoint; - this.restEndpoint = restEndpoint; - this.domain = domain; - this.projectId = projectId; - this.keyStore = keyStore; - this.trustStore = trustStore; - } - - public String getBrokerEndpoint() { - return brokerEndpoint; - } - - public String getRestEndpoint() { - return restEndpoint; - } - - public String getDomain() { - return domain; - } - - public String getProjectId() { - return projectId; - } - - public String getKeyStore() { - return keyStore; - } - - public String getTrustStore() { - return trustStore; - } - - public void setBrokerEndpoint(String brokerEndpoint) { - this.brokerEndpoint = brokerEndpoint; - } - - public void setRestEndpoint(String restEndpoint) { - this.restEndpoint = restEndpoint; - } - - public void setDomain(String domain) { - this.domain = domain; - } - - public void setProjectId(String projectId) { - this.projectId = projectId; - } - - public void setKeyStore(String keyStore) { - this.keyStore = keyStore; - } - - public void setTrustStore(String trustStore) { - this.trustStore = trustStore; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaResource.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaResource.java deleted file mode 100644 index a67e016691..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/KafkaResource.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -public class KafkaResource { - - private String sessionId; - private String topicName; - - public KafkaResource(String sessionId, String topicName) { - this.sessionId = sessionId; - this.topicName = topicName; - } - - public KafkaResource() { - - } - - public String getSessionId() { - return sessionId; - } - - public void setSessionId(String sessionId) { - this.sessionId = sessionId; - } - - public String getTopicName() { - return topicName; - } - - public void setTopicName(String topicName) { - this.topicName = topicName; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ManifestJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ManifestJSON.java deleted file mode 100644 index 0cfd378e5b..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/ManifestJSON.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import java.util.List; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class ManifestJSON { - - private String datasetName; - - private String datasetDescription; - - private String creatorEmail; - - private String creatorDate; - - private boolean kafkaSupport; - - private List fileInfos; - - private List metaDataJsons; - - public ManifestJSON() { - } - - public ManifestJSON(String datasetName, String datasetDescription, - String creatorEmail, String creatorDate, boolean kafkaSupport, - List fileInfos, List metaDataJsons) { - this.datasetName = datasetName; - this.datasetDescription = datasetDescription; - this.creatorEmail = creatorEmail; - this.creatorDate = creatorDate; - this.kafkaSupport = kafkaSupport; - this.fileInfos = fileInfos; - this.metaDataJsons = metaDataJsons; - } - - public List getFileInfos() { - return fileInfos; - } - - public void setFileInfos(List fileInfos) { - this.fileInfos = fileInfos; - } - - public List getMetaDataJsons() { - return metaDataJsons; - } - - public void setMetaDataJsons(List metaDataJsons) { - this.metaDataJsons = metaDataJsons; - } - - public String getDatasetName() { - return datasetName; - } - - public void setDatasetName(String datasetName) { - this.datasetName = datasetName; - } - - public String getDatasetDescription() { - return datasetDescription; - } - - public void setDatasetDescription(String datasetDescription) { - this.datasetDescription = datasetDescription; - } - - public String getCreatorEmail() { - return creatorEmail; - } - - public void setCreatorEmail(String creatorEmail) { - this.creatorEmail = creatorEmail; - } - - public String getCreatorDate() { - return creatorDate; - } - - public void setCreatorDate(String creatorDate) { - this.creatorDate = creatorDate; - } - - public boolean isKafkaSupport() { - return kafkaSupport; - } - - public void setKafkaSupport(boolean kafkaSupport) { - this.kafkaSupport = kafkaSupport; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/RemoveGVodJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/RemoveGVodJSON.java deleted file mode 100644 index 7f2d7b63fe..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/RemoveGVodJSON.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class RemoveGVodJSON { - - private TorrentId torrentId; - - public RemoveGVodJSON() { - } - - public RemoveGVodJSON(TorrentId torrentId) { - this.torrentId = torrentId; - } - - public TorrentId getTorrentId() { - return torrentId; - } - - public void setTorrentId(TorrentId torrentId) { - this.torrentId = torrentId; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/RemoveTorrentDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/RemoveTorrentDTO.java deleted file mode 100644 index 2d27e6a3fa..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/RemoveTorrentDTO.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class RemoveTorrentDTO { - - private String torrentId; - - public RemoveTorrentDTO() { - } - - public String getTorrentId() { - return torrentId; - } - - public void setTorrentId(String torrentId) { - this.torrentId = torrentId; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/StatusGVoDJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/StatusGVoDJSON.java deleted file mode 100644 index 7af47067f9..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/StatusGVoDJSON.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class StatusGVoDJSON { - - private TorrentId torrentId; - private String datasetPath; - - public StatusGVoDJSON() { - } - - public StatusGVoDJSON(String datasetPath, TorrentId torrentId) { - this.torrentId = torrentId; - this.datasetPath = datasetPath; - } - - public TorrentId getTorrentId() { - return torrentId; - } - - public void setTorrentId(TorrentId torrentId) { - this.torrentId = torrentId; - } - - public String getDatasetPath() { - return datasetPath; - } - - public void setDatasetPath(String datasetPath) { - this.datasetPath = datasetPath; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/SuccessJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/SuccessJSON.java deleted file mode 100644 index 277accd9ce..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/SuccessJSON.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class SuccessJSON { - - private String details; - - public SuccessJSON() { - this.details = ""; - } - - public SuccessJSON(String details) { - this.details = details; - } - - public String getDetails() { - return details; - } - - public void setDetails(String details) { - this.details = details; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/TorrentExtendedStatusJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/TorrentExtendedStatusJSON.java deleted file mode 100644 index d9db8422ed..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/TorrentExtendedStatusJSON.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class TorrentExtendedStatusJSON { - - private TorrentId torrentId; - private String torrentStatus; - private int downloadSpeed; - private double percentageCompleted; - - public TorrentExtendedStatusJSON(TorrentId torrentId, String torrentStatus, - int downloadSpeed, - double percentageCompleted) { - this.torrentId = torrentId; - this.torrentStatus = torrentStatus; - this.downloadSpeed = downloadSpeed; - this.percentageCompleted = percentageCompleted; - } - - public TorrentExtendedStatusJSON() { - } - - public String getTorrentStatus() { - return torrentStatus; - } - - public void setTorrentStatus(String torrentStatus) { - this.torrentStatus = torrentStatus; - } - - public int getDownloadSpeed() { - return downloadSpeed; - } - - public void setDownloadSpeed(int downloadSpeed) { - this.downloadSpeed = downloadSpeed; - } - - public TorrentId getTorrentId() { - return torrentId; - } - - public void setTorrentId(TorrentId torrentId) { - this.torrentId = torrentId; - } - - public double getPercentageCompleted() { - return percentageCompleted; - } - - public void setPercentageCompleted(double percentageCompleted) { - this.percentageCompleted = percentageCompleted; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/TorrentId.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/TorrentId.java deleted file mode 100644 index 9e75fe7eb8..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_dto/TorrentId.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class TorrentId { - - private String val; - - public TorrentId() { - } - - public TorrentId(String val) { - this.val = val; - } - - public String getVal() { - return val; - } - - public void setVal(String val) { - this.val = val; - } - - @Override - public String toString() { - return "TorrentId{" + "val=" + val + '}'; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/DatasetIssueDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/DatasetIssueDTO.java deleted file mode 100644 index 90ef1b7287..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/DatasetIssueDTO.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_hopssite_dto; - -import io.hops.hopsworks.dela.dto.common.UserDTO; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class DatasetIssueDTO { - - private String type; - private String msg; - private UserDTO.Complete user; - private String publicDSId; - - public DatasetIssueDTO() { - } - - public DatasetIssueDTO(String publicDSId, UserDTO.Complete user, String type, String msg) { - this.type = type; - this.msg = msg; - this.user = user; - this.publicDSId = publicDSId; - } - - public UserDTO.Complete getUser() { - return user; - } - - public void setUser(UserDTO.Complete user) { - this.user = user; - } - - public String getPublicDSId() { - return publicDSId; - } - - public void setPublicDSId(String publicDSId) { - this.publicDSId = publicDSId; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - -} \ No newline at end of file diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/PopularDatasetJSON.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/PopularDatasetJSON.java deleted file mode 100644 index 25c1bd2e3c..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/PopularDatasetJSON.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_hopssite_dto; - -import javax.xml.bind.annotation.XmlRootElement; -import io.hops.hopsworks.dela.old_dto.ManifestJSON; - -@XmlRootElement -public class PopularDatasetJSON { - - private ManifestJSON manifestJson; - - private String datasetId; - - private int leeches; - - private int seeds; - - public PopularDatasetJSON(ManifestJSON manifestJson, String datasetId, - int leeches, int seeds) { - this.manifestJson = manifestJson; - this.datasetId = datasetId; - this.leeches = leeches; - this.seeds = seeds; - } - - public PopularDatasetJSON() { - } - - public void setDatasetId(String datasetId) { - this.datasetId = datasetId; - } - - public void setLeeches(int leeches) { - this.leeches = leeches; - } - - public void setSeeds(int seeds) { - this.seeds = seeds; - } - - public ManifestJSON getManifestJson() { - return manifestJson; - } - - public void setManifestJson(ManifestJSON manifestJson) { - this.manifestJson = manifestJson; - } - - public String getDatasetId() { - return datasetId; - } - - public int getLeeches() { - return leeches; - } - - public int getSeeds() { - return seeds; - } - -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/RateIdDTO.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/RateIdDTO.java deleted file mode 100644 index 78eae26c89..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/old_hopssite_dto/RateIdDTO.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.old_hopssite_dto; - -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement -public class RateIdDTO { - private Integer id; - - public RateIdDTO() { - } - - public RateIdDTO(Integer id) { - this.id = id; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/util/ManifestHelper.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/util/ManifestHelper.java deleted file mode 100644 index 27c0f04cfc..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/dela/util/ManifestHelper.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.dela.util; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import io.hops.hopsworks.restutils.RESTCodes; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.dela.old_dto.ManifestJSON; - -import java.io.UnsupportedEncodingException; -import java.util.logging.Level; - -public class ManifestHelper { - public static byte[] marshall(ManifestJSON manifest) throws DelaException { - Gson gson = new GsonBuilder().create(); - String jsonString = gson.toJson(manifest); - byte[] jsonByte; - try { - jsonByte = jsonString.getBytes("UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.MANIFEST_ENCODING_ERROR, Level.SEVERE, DelaException.Source.LOCAL, - null, ex.getMessage(), ex); - } - return jsonByte; - } - - public static ManifestJSON unmarshall(byte[] jsonByte) throws DelaException { - String jsonString; - try { - jsonString = new String(jsonByte, "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new DelaException(RESTCodes.DelaErrorCode.MANIFEST_ENCODING_ERROR, Level.SEVERE, DelaException.Source.LOCAL, - null, ex.getMessage(), ex); - } - ManifestJSON manifest = new Gson().fromJson(jsonString, ManifestJSON.class); - return manifest; - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/util/CertificateHelper.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/util/CertificateHelper.java deleted file mode 100644 index 1f1afbcafe..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/util/CertificateHelper.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.util; - -import com.google.common.io.ByteStreams; -import io.hops.hopsworks.common.dao.dela.certs.ClusterCertificateFacade; -import io.hops.hopsworks.common.security.CertificatesMgmService; -import io.hops.hopsworks.common.util.HopsUtils; -import io.hops.hopsworks.common.util.LocalhostServices; -import io.hops.hopsworks.common.util.OSProcessExecutor; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.persistence.entity.dela.certs.ClusterCertificate; -import org.apache.commons.io.FileUtils; -import org.javatuples.Triplet; - -import javax.security.auth.x500.X500Principal; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class CertificateHelper { - - private final static Logger LOG = Logger.getLogger(CertificateHelper.class.getName()); - - public static Optional> loadKeystoreFromFile(String masterPswd, Settings settings, - ClusterCertificateFacade certFacade, CertificatesMgmService certificatesMgmService, - OSProcessExecutor osProcessExecutor) { - String certPath = settings.getHopsSiteCert(); - String intermediateCertPath = settings.getHopsSiteIntermediateCert(); - String keystorePath = settings.getHopsSiteKeyStorePath(); - String truststorePath = settings.getHopsSiteTrustStorePath(); - try { - String certPswd = HopsUtils.randomString(64); - String encryptedCertPswd = HopsUtils.encrypt(masterPswd, certPswd, - certificatesMgmService.getMasterEncryptionPassword()); - File certFile = readFile(certPath); - File intermediateCertFile = readFile(intermediateCertPath); - String clusterName = getClusterName(certFile); - settings.setHopsSiteClusterName(clusterName); - generateKeystore(certFile, intermediateCertFile, certPswd, settings, osProcessExecutor); - File keystoreFile = readFile(keystorePath); - File truststoreFile = readFile(truststorePath); - KeyStore keystore, truststore; - try (FileInputStream keystoreIS = new FileInputStream(keystoreFile); - FileInputStream truststoreIS = new FileInputStream(truststoreFile)) { - keystore = keystore(keystoreIS, certPswd); - truststore = keystore(truststoreIS, certPswd); - } - try (FileInputStream keystoreIS = new FileInputStream(keystoreFile); - FileInputStream truststoreIS = new FileInputStream(truststoreFile)) { - certFacade.saveClusterCerts(clusterName, ByteStreams.toByteArray(keystoreIS), - ByteStreams.toByteArray(truststoreIS), encryptedCertPswd); - } - return Optional.of(Triplet.with(keystore, truststore, certPswd)); - } catch (Exception ex) { - settings.deleteHopsSiteClusterName(); - LOG.log(Level.SEVERE, "keystore ex. {0}", ex.getMessage()); - return Optional.empty(); - } finally { - FileUtils.deleteQuietly(new File(keystorePath)); - FileUtils.deleteQuietly(new File(truststorePath)); - } - } - - public static Optional> loadKeystoreFromDB(String masterPswd, String clusterName, - ClusterCertificateFacade certFacade, CertificatesMgmService certificatesMgmService) { - try { - Optional cert = certFacade.getClusterCert(clusterName); - if (!cert.isPresent()) { - return Optional.empty(); - } - String certPswd = HopsUtils.decrypt(masterPswd, cert.get().getCertificatePassword(), - certificatesMgmService.getMasterEncryptionPassword()); - KeyStore keystore, truststore; - try (ByteArrayInputStream keystoreIS = new ByteArrayInputStream(cert.get().getClusterKey()); - ByteArrayInputStream truststoreIS = new ByteArrayInputStream(cert.get().getClusterCert())) { - keystore = keystore(keystoreIS, certPswd); - truststore = keystore(truststoreIS, certPswd); - } - return Optional.of(Triplet.with(keystore, truststore, certPswd)); - } catch (Exception ex) { - LOG.log(Level.SEVERE, "keystore ex. {0}", ex.getMessage()); - return Optional.empty(); - } - } - - private static void generateKeystore(File cert, File intermediateCert, String certPswd, Settings settings, - OSProcessExecutor osProcessExecutor) throws IllegalStateException { - if (!isCertSigned(cert, intermediateCert)) { - throw new IllegalStateException("Certificate is not signed"); - } - try { - LocalhostServices.generateHopsSiteKeystore(settings, osProcessExecutor, certPswd); - } catch (IOException ex) { - LOG.log(Level.SEVERE, "keystore generate ex. {0}", ex.getMessage()); - throw new IllegalStateException("keystore generate ex", ex); - } - } - - private static String getClusterName(File certFile) throws IllegalStateException { - X509Certificate cert = getX509Cert(certFile); - String o = getCertificatePart(cert, "O"); - String ou = getCertificatePart(cert, "OU"); - String clusterName = o + "_" + ou; - return clusterName; - } - - private static X509Certificate getX509Cert(File cert) throws IllegalStateException { - try (InputStream inStream = new FileInputStream(cert)) { - CertificateFactory factory = CertificateFactory.getInstance("X.509"); - X509Certificate x509Cert = (X509Certificate) factory.generateCertificate(inStream); - return x509Cert; - } catch (CertificateException | IOException ex) { - LOG.log(Level.SEVERE, "cert ex {0}", ex); - throw new IllegalStateException("cert ex", ex); - } - } - - private static boolean isCertSigned(File certFile, File intermediateCertFile) throws IllegalStateException { - X509Certificate cert = getX509Cert(certFile); - X509Certificate caCert = getX509Cert(intermediateCertFile); - String intermediateSubjectDN = caCert.getSubjectDN().getName(); - String issuerDN = cert.getIssuerDN().getName(); - LOG.log(Level.INFO, "sign check: {0} {1}", new Object[]{issuerDN, intermediateSubjectDN}); - return issuerDN.equals(intermediateSubjectDN); - } - - private static File readFile(String certPath) throws IllegalStateException { - File certFile = new File(certPath); - if (!certFile.exists()) { - LOG.log(Level.SEVERE, "Could not find file:{0}", certPath); - throw new IllegalStateException("Could not find file"); - } - return certFile; - } - - private static KeyStore keystore(InputStream is, String certPswd) throws IllegalStateException { - try { - KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); - keystore.load(is, certPswd.toCharArray()); - return keystore; - } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException ex) { - LOG.log(Level.SEVERE, "keystore ex. {0}", ex); - throw new IllegalStateException("keystore ex", ex); - } - } - - public static String getCertificatePart(X509Certificate cert, String partName) { - String tmpName, name = ""; - X500Principal principal = cert.getSubjectX500Principal(); - String part = partName + "="; - int start = principal.getName().indexOf(part); - if (start > -1) { - tmpName = principal.getName().substring(start + part.length()); - int end = tmpName.indexOf(","); - if (end > 0) { - name = tmpName.substring(0, end); - } else { - name = tmpName; - } - } - return name.toLowerCase(); - } -} diff --git a/hopsworks-dela/src/main/java/io/hops/hopsworks/util/SettingsHelper.java b/hopsworks-dela/src/main/java/io/hops/hopsworks/util/SettingsHelper.java deleted file mode 100644 index 1e2c8b3a28..0000000000 --- a/hopsworks-dela/src/main/java/io/hops/hopsworks/util/SettingsHelper.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.util; - -import io.hops.hopsworks.common.dao.user.UserFacade; -import io.hops.hopsworks.common.dela.AddressJSON; -import io.hops.hopsworks.common.util.Settings; -import io.hops.hopsworks.exceptions.DelaException; -import io.hops.hopsworks.persistence.entity.user.Users; -import io.hops.hopsworks.restutils.RESTCodes; - -import java.util.logging.Level; - -public class SettingsHelper { - - public static AddressJSON delaTransferEndpoint(Settings settings) throws DelaException { - AddressJSON delaTransferEndpoint = settings.getDELA_PUBLIC_ENDPOINT(); - if (delaTransferEndpoint == null) { - throw new DelaException(RESTCodes.DelaErrorCode.MISCONFIGURED, Level.FINE, DelaException.Source.SETTINGS, - "DELA_TRANSFER_ENDPOINT"); - } - return delaTransferEndpoint; - } - - public static String delaTransferHttpEndpoint(Settings settings) throws DelaException { - String delaTransferHttpEndpoint = settings.getDELA_TRANSFER_HTTP_ENDPOINT(); - if (delaTransferHttpEndpoint == null) { - throw new DelaException(RESTCodes.DelaErrorCode.MISCONFIGURED, Level.FINE, DelaException.Source.SETTINGS, - "DELA_TRANSFER_HTTP_ENDPOINT"); - } - return delaTransferHttpEndpoint; - } - - public static String delaHttpEndpoint(Settings settings) throws DelaException { - String delaHttpEndpoint = settings.getDELA_SEARCH_ENDPOINT(); - if (delaHttpEndpoint == null) { - throw new DelaException(RESTCodes.DelaErrorCode.MISCONFIGURED, Level.FINE, DelaException.Source.SETTINGS, - "DELA_HTTP_ENDPOINT"); - } - return delaHttpEndpoint; - } - - public static String clusterId(Settings settings) throws DelaException { - String clusterId = settings.getDELA_CLUSTER_ID(); - if (clusterId == null) { - throw new DelaException(RESTCodes.DelaErrorCode.MISCONFIGURED, Level.FINE, DelaException.Source.SETTINGS, - "DELA_CLUSTER_ID"); - } - return clusterId; - } - - public static String hopsSite(Settings settings) throws DelaException { - String hopsSite = settings.getHOPSSITE(); - if (hopsSite == null) { - throw new DelaException(RESTCodes.DelaErrorCode.MISCONFIGURED, Level.FINE, DelaException.Source.SETTINGS, - "DELA_HOPS_SITE"); - } - return hopsSite; - } - - public static String hopsSiteHost(Settings settings) throws DelaException { - String hopsSiteHost = settings.getHOPSSITE_HOST(); - if (hopsSiteHost == null) { - throw new DelaException(RESTCodes.DelaErrorCode.MISCONFIGURED, Level.FINE, DelaException.Source.SETTINGS, - "DELA_HOPS_SITE_HOST"); - } - return hopsSiteHost; - } - - public static Users getUser(UserFacade userFacade, String email) throws DelaException { - Users user = userFacade.findByEmail(email); - if (user == null) { - throw new DelaException(RESTCodes.DelaErrorCode.USER_NOT_FOUND, Level.FINE, DelaException.Source.LOCAL); - } - return user; - } -} diff --git a/hopsworks-dela/src/main/resources/META-INF/MANIFEST.MF b/hopsworks-dela/src/main/resources/META-INF/MANIFEST.MF deleted file mode 100644 index 59499bce4a..0000000000 --- a/hopsworks-dela/src/main/resources/META-INF/MANIFEST.MF +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/hopsworks-ear/pom.xml b/hopsworks-ear/pom.xml index ba7e2f1bbb..47a1e64229 100644 --- a/hopsworks-ear/pom.xml +++ b/hopsworks-ear/pom.xml @@ -79,12 +79,6 @@ ${project.version} ejb - - io.hops.hopsworks - hopsworks-dela - ${project.version} - ejb - io.hops.hopsworks hopsworks-jwt @@ -117,21 +111,6 @@ - - hops-site - - false - - - - io.hops.hopsworks - hopsworks-cluster - ${project.version} - war - - - - testing @@ -147,4 +126,4 @@ - \ No newline at end of file + diff --git a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/DelaCSRCheckException.java b/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/DelaCSRCheckException.java deleted file mode 100644 index 4b094d1a26..0000000000 --- a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/DelaCSRCheckException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - */ - -package io.hops.hopsworks.exceptions; - -import io.hops.hopsworks.restutils.RESTCodes; -import io.hops.hopsworks.restutils.RESTException; - -import java.util.logging.Level; - -public class DelaCSRCheckException extends RESTException { - public DelaCSRCheckException(RESTCodes.DelaCSRErrorCode code, Level level) { - super(code, level); - } - - public DelaCSRCheckException(RESTCodes.DelaCSRErrorCode code, Level level, String usrMsg) { - super(code, level, usrMsg); - } - - public DelaCSRCheckException(RESTCodes.DelaCSRErrorCode code, Level level, String usrMsg, String devMsg) { - super(code, level, usrMsg, devMsg); - } - - public DelaCSRCheckException(RESTCodes.DelaCSRErrorCode code, Level level, String usrMsg, String devMsg, - Throwable throwable) { - super(code, level, usrMsg, devMsg, throwable); - } -} diff --git a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/DelaException.java b/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/DelaException.java deleted file mode 100644 index 5c62532750..0000000000 --- a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/exceptions/DelaException.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Changes to this file committed after and not including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * This file is part of Hopsworks - * Copyright (C) 2018, Logical Clocks AB. All rights reserved - * - * Hopsworks is free software: you can redistribute it and/or modify it under the terms of - * the GNU Affero General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * Hopsworks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License along with this program. - * If not, see . - * - * Changes to this file committed before and including commit-id: ccc0d2c5f9a5ac661e60e6eaf138de7889928b8b - * are released under the following license: - * - * Copyright (C) 2013 - 2018, Logical Clocks AB and RISE SICS AB. All rights reserved - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit - * persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING - * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package io.hops.hopsworks.exceptions; - -import io.hops.hopsworks.restutils.RESTCodes; -import io.hops.hopsworks.restutils.RESTException; - -import java.util.logging.Level; - -public class DelaException extends RESTException { - - private final Source source; - - public DelaException(RESTCodes.DelaErrorCode code, Level level, Source source) { - this(code, level, source, null); - } - - public DelaException(RESTCodes.DelaErrorCode code, Level level, Source source, String usrMsg) { - this(code, level, source, usrMsg, null); - } - - public DelaException(RESTCodes.DelaErrorCode code, Level level, Source source, String usrMsg, String devMsg) { - this(code, level, source, usrMsg, devMsg, null); - } - - public DelaException(RESTCodes.DelaErrorCode code, Level level, Source source, String usrMsg, String devMsg, - Throwable throwable) { - super(code, level, usrMsg, devMsg, throwable); - this.source = source; - } - - public Source getSource() { - return source; - } - - public enum Source { - LOCAL, - SETTINGS, - MYSQL, - HDFS, - KAFKA, - DELA, - REMOTE_DELA, - HOPS_SITE - } - - @Override - public String toString() { - return "{" + - "source=" + source + - super.toString() + - '}'; - } -} diff --git a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/RESTCodes.java b/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/RESTCodes.java index 1265490846..9b5a33b613 100644 --- a/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/RESTCodes.java +++ b/hopsworks-rest-utils/src/main/java/io/hops/hopsworks/restutils/RESTCodes.java @@ -1009,67 +1009,6 @@ public int getRange() { } } - public enum DelaErrorCode implements RESTErrorCode { - - //response for validation error - THIRD_PARTY_ERROR(0, "Generic third party error.", Response.Status.INTERNAL_SERVER_ERROR), - CLUSTER_NOT_REGISTERED(1, "Cluster not registered.", Response.Status.UNAUTHORIZED), - HEAVY_PING(2, "Heavy ping required.", Response.Status.BAD_REQUEST), - USER_NOT_REGISTERED(3, "User not registered.", Response.Status.UNAUTHORIZED), - DATASET_EXISTS(4, "Dataset exists.", Response.Status.CONFLICT), - DATASET_DOES_NOT_EXIST(5, "Dataset does not exist.", Response.Status.NOT_FOUND), - DATASET_PUBLISH_PERMISSION_ERROR(6, "Dataset shared - only owner can publish.", - Response.Status.FORBIDDEN), - ILLEGAL_ARGUMENT(7, "Illegal Argument.", Response.Status.BAD_REQUEST), - README_RETRIEVAL_FAILED(8, "Readme retrieval failed.", Response.Status.EXPECTATION_FAILED), - README_ACCESS_PROBLEM(9, "Readme access problem.", Response.Status.BAD_REQUEST), - COMMUNICATION_FAILURE(10, "Communication failure.", Response.Status.EXPECTATION_FAILED), - DATASET_EMPTY(11, "Dataset empty.", Response.Status.BAD_REQUEST), - SUBDIRS_NOT_SUPPORTED(12, "Subdirectories not supported.", Response.Status.BAD_REQUEST), - ACCESS_ERROR(13, "Access error.", Response.Status.BAD_REQUEST), - MISCONFIGURED(14, "misconfigured", Response.Status.EXPECTATION_FAILED), - USER_NOT_FOUND(15, "user not found.", Response.Status.FORBIDDEN), - DATASET_DELETE_ERROR(16, "Delete dataset error.", Response.Status.EXPECTATION_FAILED), - DELA_NOT_AVAILABLE(17, "Dela not available.", Response.Status.BAD_REQUEST), - HOPSSITE_NOT_AVAILABLE(18, "hopssite not available", Response.Status.BAD_REQUEST), - REMOTE_DELA_NOT_AVAILABLE(19, "Remote dela not available.", Response.Status.BAD_REQUEST), - DELA_TRANSFER_NOT_AVAILABLE(20, "Dela transfer not available.", Response.Status.BAD_REQUEST), - MANIFEST_ENCODING_ERROR(21, "Manifest cannot be read as UTF", Response.Status.BAD_REQUEST), - DATASET_NOT_PUBLIC(22, "dataset not public - no manifest", Response.Status.BAD_REQUEST), - INODE_NOT_FOUND(23, "Inode not found.", Response.Status.BAD_REQUEST); - - private Integer code; - private String message; - private Response.StatusType respStatus; - public final int range = 170000; - - - DelaErrorCode(Integer code, String message, Response.StatusType respStatus) { - this.code = range + code; - this.message = message; - this.respStatus = respStatus; - } - - @Override - public Integer getCode() { - return code; - } - - @Override - public String getMessage() { - return message; - } - - public Response.StatusType getRespStatus() { - return respStatus; - } - - @Override - public int getRange() { - return range; - } - } - public enum CAErrorCode implements RESTErrorCode { BADSIGNREQUEST(0, "No CSR provided or CSR is malformed", Response.Status.BAD_REQUEST), diff --git a/pom.xml b/pom.xml index ed3805223d..8ce2d3fa1e 100755 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,6 @@ hopsworks-api hopsworks-ear hopsworks-ca - hopsworks-dela hopsworks-jwt hopsworks-persistence hopsworks-IT @@ -96,7 +95,6 @@ 2.8.0 1.32.0 1.32.0 - 3.2.2 1.4 3.1 2.11.0 @@ -109,8 +107,6 @@ 1.14.3 1.14.3.0 2.3.29 - - 4.10.14 2.2 19.0 3.0.0 @@ -124,7 +120,6 @@ 7.0 1.2 1.1 - 1.4.7 2.0.1.Final 2.26 2.26 @@ -271,11 +266,6 @@ bijection-avro_2.12 ${bijection-avro_2.12.version} - - commons-collections - commons-collections - ${commons-collections.version} - commons-fileupload commons-fileupload @@ -582,12 +572,6 @@ ${project.version} ejb - - io.hops.hopsworks - hopsworks-dela - ${project.version} - ejb - io.hops.hopsworks hopsworks-jwt @@ -650,11 +634,6 @@ ${javax-enterprise-concurrent-api.version} provided - - javax.mail - mail - ${javax-mail.version} - javax.validation validation-api @@ -846,11 +825,6 @@ freemarker ${freemarker.version} - - org.gitlab4j - gitlab4j-api - ${gitlab4j-api.version} - org.glassfish.jersey.core jersey-client @@ -1105,15 +1079,6 @@ - - cluster - - false - - - hopsworks-cluster - - web