diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a25266110..400398fd4e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,7 @@ env: # https://maven.apache.org/resolver/configuration.html MAVEN_OPTS: > -Daether.connector.http.connectionMaxTtl=25 + -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 jobs: compile: @@ -37,7 +38,7 @@ jobs: strategy: matrix: # Minimum Java version! - java: [11] + java: [17] steps: - uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.java }} @@ -62,10 +63,10 @@ jobs: test: needs: compile runs-on: ubuntu-latest - timeout-minutes: 20 + timeout-minutes: 30 strategy: matrix: - java: [11, 14, 17] + java: [17] steps: - uses: actions/checkout@v4 - name: Set up JDK @@ -74,9 +75,11 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java }} cache: 'maven' + timeout-minutes: 2 - name: Build with Java ${{ matrix.java }} run: mvn install --settings $SETTINGS_FILE -DskipTests=true -Dmaven.javadoc.skip=true + timeout-minutes: 3 - name: "Check: Test" run: mvn verify --settings $SETTINGS_FILE -Dmaven.javadoc.skip=true jacoco:report - name: "Report: Coverage via coveralls.io" @@ -91,8 +94,8 @@ jobs: CI_BUILD_URL: https://github.com/${{ github.repository }}/commit/${{ github.event.after }}/checks COVERALLS_SECRET: ${{ secrets.GITHUB_TOKEN }} continue-on-error: true - - name: Upload Build (JDK 11 only) - if: matrix.java == 11 + - name: Upload Build (JDK 17 only) + if: matrix.java == 17 uses: actions/upload-artifact@v3 with: name: spinnaker-exe.jar @@ -100,7 +103,7 @@ jobs: retention-days: 5 continue-on-error: true - name: Submit Dependency Snapshot - if: matrix.java == 11 + if: matrix.java == 17 uses: advanced-security/maven-dependency-submission-action@v3 continue-on-error: true @@ -110,7 +113,7 @@ jobs: timeout-minutes: 5 strategy: matrix: - java: [11, 14] + java: [17] steps: - uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.java }} @@ -134,11 +137,11 @@ jobs: timeout-minutes: 5 steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3.13.0 with: distribution: 'zulu' - java-version: 11 + java-version: 17 cache: 'maven' - name: "Check: No FIXMEs left" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 78a28490fb..9a154ce078 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - version: [ 11 ] + version: [ 17 ] steps: - name: Checkout repository @@ -79,7 +79,8 @@ jobs: # queries: ./path/to/local/query, your-org/your-repo/queries@main # Set up right Java version: https://github.com/github/codeql-action/issues/825 - - uses: actions/setup-java@v3.13.0 + - name: Initialize Java ${{ matrix.version }} + uses: actions/setup-java@v3.13.0 with: java-version: ${{ matrix.version }} distribution: zulu diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a31d2b50cf..efdb4a39ee 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -33,7 +33,7 @@ jobs: timeout-minutes: 10 strategy: matrix: - java: [14] + java: [17] env: SITE_DIR: target/staging diff --git a/SpiNNaker-allocserv/pom.xml b/SpiNNaker-allocserv/pom.xml index 5da6a8e97f..8647ae81fa 100644 --- a/SpiNNaker-allocserv/pom.xml +++ b/SpiNNaker-allocserv/pom.xml @@ -41,6 +41,7 @@ limitations under the License. ${project.build.directory}/generated-resources/truststore.p12 hunter2 build + 6.1.4 @@ -255,10 +256,12 @@ limitations under the License. - org.apache.sling + io.leonard.maven.plugins jspc-maven-plugin - ${project.basedir}/src/main/webapp/WEB-INF + + ${project.basedir}/src/main/webapp/WEB-INF/**/*.jsp + @@ -267,6 +270,14 @@ limitations under the License. + + + org.springframework.security + spring-security-bom + ${spring-security.version} + pom + import + org.springframework @@ -291,12 +302,6 @@ limitations under the License. pom import - - - org.springframework - spring-webmvc - ${spring.version} - @@ -353,6 +358,11 @@ limitations under the License. org.apache.cxf cxf-rt-frontend-jaxrs + + + jakarta.xml.ws + jakarta.xml.ws-api + org.springframework.boot spring-boot-starter-web @@ -370,12 +380,12 @@ limitations under the License. spring-boot-starter-security - org.springframework.boot - spring-boot-starter-jdbc + com.fasterxml.jackson.jakarta.rs + jackson-jakarta-rs-json-provider - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider + org.springframework.boot + spring-boot-starter-jdbc org.springframework.boot @@ -389,10 +399,14 @@ limitations under the License. - javax - javaee-api + jakarta.servlet + jakarta.servlet-api provided + + jakarta.mail + jakarta.mail-api + org.slf4j slf4j-api @@ -413,11 +427,6 @@ limitations under the License. org.apache.cxf cxf-rt-rs-service-description-openapi-v3 - - - jakarta.xml.ws - jakarta.xml.ws-api - org.webjars swagger-ui @@ -438,8 +447,8 @@ limitations under the License. jakarta.servlet.jsp.jstl - javax.servlet - jstl + jakarta.servlet.jsp.jstl + jakarta.servlet.jsp.jstl-api org.springframework.security @@ -534,14 +543,14 @@ limitations under the License. - org.apache.sling + io.leonard.maven.plugins jspc-maven-plugin + jspc - jspc + compile - compile diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/ServiceConfig.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/ServiceConfig.java index 5327fda26d..e3875281f2 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/ServiceConfig.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/ServiceConfig.java @@ -17,10 +17,10 @@ import static com.fasterxml.jackson.databind.PropertyNamingStrategies.KEBAB_CASE; import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS; +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; +import static jakarta.ws.rs.core.Response.status; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static java.lang.System.setProperty; -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; -import static javax.ws.rs.core.Response.status; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static org.apache.cxf.message.Message.PROTOCOL_HEADERS; import static org.apache.cxf.phase.Phase.RECEIVE; import static org.apache.cxf.transport.http.AbstractHTTPDestination.HTTP_REQUEST; @@ -33,15 +33,7 @@ import java.util.Map; import java.util.concurrent.Executor; -import javax.annotation.PostConstruct; -import javax.servlet.ServletRequest; import javax.sql.DataSource; -import javax.validation.ValidationException; -import javax.ws.rs.ApplicationPath; -import javax.ws.rs.core.Application; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; import org.apache.cxf.bus.spring.SpringBus; import org.apache.cxf.endpoint.Server; @@ -82,8 +74,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; - +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; + +import jakarta.annotation.PostConstruct; +import jakarta.servlet.ServletRequest; +import jakarta.validation.ValidationException; +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.AllocatorProperties; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.AuthProperties; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.HistoricalDataProperties; @@ -121,6 +123,7 @@ public class ServiceConfig extends Application { @Bean(name = "mainDatasource") @Role(ROLE_INFRASTRUCTURE) + @Primary @ConfigurationProperties(prefix = "spalloc.datasource") DataSource mainDatasource() { return DataSourceBuilder.create().build(); @@ -143,8 +146,7 @@ DataSource historicalDatasource() { */ @Bean(name = "mainDatabase") @Role(ROLE_SUPPORT) - public JdbcTemplate mainDatabase( - @Qualifier("mainDatasource") DataSource ds) { + JdbcTemplate mainDatabase(@Qualifier("mainDatasource") DataSource ds) { return new JdbcTemplate(ds); } @@ -158,7 +160,7 @@ public JdbcTemplate mainDatabase( */ @Bean(name = "historicalDatabase") @Role(ROLE_SUPPORT) - public JdbcTemplate historicalDatabase( + JdbcTemplate historicalDatabase( @Qualifier("historicalDatasource") DataSource ds) { return new JdbcTemplate(ds); } @@ -173,7 +175,7 @@ public JdbcTemplate historicalDatabase( */ @Bean(name = "mainTransactionManager") @Role(ROLE_SUPPORT) - public PlatformTransactionManager mainTransactionManager( + PlatformTransactionManager mainTransactionManager( @Qualifier("mainDatasource") DataSource ds) { return new JdbcTransactionManager(ds); } @@ -210,6 +212,8 @@ public ThreadPoolTaskScheduler threadPoolTaskExecutor( @Role(ROLE_INFRASTRUCTURE) JsonMapper mapper() { return JsonMapper.builder().findAndAddModules() + .addModule(new JavaTimeModule()) + .addModule(new Jdk8Module()) .disable(WRITE_DATES_AS_TIMESTAMPS) .propertyNamingStrategy(KEBAB_CASE).build(); } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/ServiceVersion.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/ServiceVersion.java index 175587ffb0..787ea0a7b0 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/ServiceVersion.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/ServiceVersion.java @@ -45,7 +45,7 @@ public class ServiceVersion { public ServiceVersion(@Value("${version}") String version, @Value("${build-timestamp}") String buildTimestamp) { fullVersion = version; - v = new Version(version.replaceAll("-.*", "")); + v = Version.parse(version.replaceAll("-.*", "")); this.buildTimestamp = buildTimestamp; } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/SpallocProperties.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/SpallocProperties.java index 710ae64975..5e1274aa3e 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/SpallocProperties.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/SpallocProperties.java @@ -23,25 +23,24 @@ import java.util.Set; import javax.sql.DataSource; -import javax.validation.Valid; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.Email; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.ConstructorBinding; import org.springframework.boot.context.properties.bind.DefaultValue; import org.springframework.core.io.Resource; import org.springframework.validation.annotation.Validated; import com.google.errorprone.annotations.Keep; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; import uk.ac.manchester.spinnaker.utils.validation.IPAddress; import uk.ac.manchester.spinnaker.utils.validation.TCPPort; @@ -53,7 +52,6 @@ * @author Donal Fellows */ @ConfigurationProperties("spalloc") -@ConstructorBinding @Validated public class SpallocProperties { /** Path to the main database file. */ diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminAPI.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminAPI.java index c461b24fdd..daeb8de0b6 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminAPI.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminAPI.java @@ -15,10 +15,10 @@ */ package uk.ac.manchester.spinnaker.alloc.admin; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static java.util.Objects.nonNull; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static uk.ac.manchester.spinnaker.alloc.admin.AdminAPI.Paths.BOARD; import static uk.ac.manchester.spinnaker.alloc.admin.AdminAPI.Paths.GROUP; import static uk.ac.manchester.spinnaker.alloc.admin.AdminAPI.Paths.IMPORT; @@ -30,26 +30,25 @@ import java.net.URI; import java.util.Map; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -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.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.core.UriInfo; - import org.springframework.security.access.prepost.PreAuthorize; import io.swagger.v3.oas.annotations.Hidden; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.model.GroupRecord; import uk.ac.manchester.spinnaker.alloc.model.MemberRecord; import uk.ac.manchester.spinnaker.alloc.model.UserRecord; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminController.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminController.java index 918370f81e..1a76585c9f 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminController.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminController.java @@ -20,14 +20,6 @@ import java.security.Principal; import java.util.Objects; -import javax.validation.Valid; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.ui.ModelMap; @@ -45,6 +37,13 @@ import com.google.errorprone.annotations.Keep; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.alloc.model.BoardRecord; import uk.ac.manchester.spinnaker.alloc.model.BoardTemperatures; import uk.ac.manchester.spinnaker.alloc.model.GroupRecord; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminControllerImpl.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminControllerImpl.java index c6f88cbef2..77d68b3760 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminControllerImpl.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminControllerImpl.java @@ -65,21 +65,22 @@ import static uk.ac.manchester.spinnaker.alloc.model.GroupRecord.GroupType.INTERNAL; import static uk.ac.manchester.spinnaker.alloc.model.GroupRecord.GroupType.ORGANISATION; import static uk.ac.manchester.spinnaker.alloc.security.SecurityConfig.IS_ADMIN; +import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.CHANGE_PASSWORD_PATH; import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.CHANGE_PASSWORD_URI; +import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.LOGOUT_PATH; import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.LOGOUT_URI; import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.MAIN_URI; -import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.SPALLOC_CSS_URI; -import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.SPALLOC_JS_URI; -import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.CHANGE_PASSWORD_PATH; -import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.LOGOUT_PATH; import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.SPALLOC_CSS_PATH; +import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.SPALLOC_CSS_URI; import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.SPALLOC_JS_PATH; +import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.SPALLOC_JS_URI; import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.error; import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.errorMessage; import static uk.ac.manchester.spinnaker.alloc.web.ControllerUtils.uri; import static uk.ac.manchester.spinnaker.alloc.web.SystemController.USER_MAY_CHANGE_PASSWORD; import java.io.IOException; +import java.io.Serial; import java.net.URI; import java.security.Principal; import java.util.HashMap; @@ -87,9 +88,6 @@ import java.util.Map; import java.util.Optional; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response.Status; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; @@ -106,6 +104,8 @@ import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response.Status; import uk.ac.manchester.spinnaker.alloc.ServiceConfig.URLPathMaker; import uk.ac.manchester.spinnaker.alloc.admin.MachineDefinitionLoader.Machine; import uk.ac.manchester.spinnaker.alloc.admin.MachineStateControl.BoardState; @@ -330,6 +330,7 @@ ModelAndView dbException(DataAccessException e, HandlerMethod hm) { } private static class AdminException extends RuntimeException { + @Serial private static final long serialVersionUID = 8401068773689159840L; AdminException(String message) { @@ -338,6 +339,7 @@ private static class AdminException extends RuntimeException { } private static final class NoUser extends AdminException { + @Serial private static final long serialVersionUID = 6430674580385445089L; private NoUser() { @@ -346,6 +348,7 @@ private NoUser() { } private static final class NoGroup extends AdminException { + @Serial private static final long serialVersionUID = -4593707687103047377L; private NoGroup() { @@ -354,6 +357,7 @@ private NoGroup() { } private static final class NoBoard extends AdminException { + @Serial private static final long serialVersionUID = -4017368969526085002L; private NoBoard() { @@ -640,8 +644,8 @@ public ModelAndView removeUserFromGroup(int id, int userid, public ModelAndView adjustGroupQuota(int id, int delta, RedirectAttributes attrs) { quotaManager.addQuota(id, delta * BOARD_HOUR).ifPresent(aq -> { - log.info("adjusted quota for group {} to {}", aq.getName(), - aq.getQuota()); + log.info("adjusted quota for group {} to {}", aq.name(), + aq.quota()); // addNotice(attrs, "quota updated"); }); return redirectTo(showGroupInfoUrl(id), attrs); @@ -713,7 +717,6 @@ public ModelAndView board(BoardRecord board, ModelMap model) { @Override @Action("saving changes to a board blacklist") public ResponseEntity blacklistSave(BlacklistData bldata) { - if (bldata.isPresent()) { try { var blacklist = bldata.getParsedBlacklist(); @@ -731,9 +734,8 @@ public ResponseEntity blacklistSave(BlacklistData bldata) { @Override @Action("fetching a live board blacklist from the machine") public BlacklistData blacklistFetch(int boardId, int bmpId) { - log.info("pulling blacklist from board {}", boardId); - BlacklistData data = new BlacklistData(); + var data = new BlacklistData(); data.setBoardId(boardId); data.setBmpId(bmpId); machineController.pullBlacklist(boardId, bmpId).ifPresentOrElse(bl -> { diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminImpl.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminImpl.java index 4bdc60c89c..205e5d065b 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminImpl.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/AdminImpl.java @@ -15,13 +15,13 @@ */ package uk.ac.manchester.spinnaker.alloc.admin; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.Response.created; +import static jakarta.ws.rs.core.Response.noContent; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; +import static jakarta.ws.rs.core.Response.Status.NOT_MODIFIED; import static java.util.stream.Collectors.toList; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static javax.ws.rs.core.Response.created; -import static javax.ws.rs.core.Response.noContent; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; -import static javax.ws.rs.core.Response.Status.NOT_FOUND; -import static javax.ws.rs.core.Response.Status.NOT_MODIFIED; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.alloc.admin.AdminAPI.Paths.BASE_PATH; @@ -29,12 +29,6 @@ import java.net.URI; import java.util.Map; -import javax.ws.rs.Path; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.core.UriInfo; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jmx.export.annotation.ManagedOperation; @@ -42,6 +36,11 @@ import org.springframework.stereotype.Service; import io.swagger.v3.oas.annotations.Hidden; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.admin.MachineDefinitionLoader.Machine; import uk.ac.manchester.spinnaker.alloc.model.GroupRecord; import uk.ac.manchester.spinnaker.alloc.model.MemberRecord; @@ -74,7 +73,7 @@ public class AdminImpl implements AdminAPI { @Override public void importMachinesByContent( MachineDefinitionLoader.Configuration definitions) { - log.warn("CALLED importMachinesByContent({})", definitions.getMachines() + log.warn("CALLED importMachinesByContent({})", definitions.machines() .stream().map(Machine::getName).collect(toList())); loader.loadMachineDefinitions(definitions); } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/DirInfo.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/DirInfo.java index 4fbf514e01..9caae47b12 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/DirInfo.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/DirInfo.java @@ -99,11 +99,7 @@ public static DirInfo get(int z, Direction direction) { @Override public boolean equals(Object o) { - if (!(o instanceof DirInfo)) { - return false; - } - var di = (DirInfo) o; - return z == di.z && dir == di.dir; + return (o instanceof DirInfo di) && (z == di.z) && (dir == di.dir); } @Override diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/MachineDefinitionLoader.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/MachineDefinitionLoader.java index 779dabeb23..48a1fd6630 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/MachineDefinitionLoader.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/MachineDefinitionLoader.java @@ -26,6 +26,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.Serial; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -33,15 +34,6 @@ import java.util.Optional; import java.util.Set; -import javax.annotation.PostConstruct; -import javax.validation.Valid; -import javax.validation.ValidatorFactory; -import javax.validation.constraints.AssertFalse; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -57,6 +49,14 @@ import com.google.errorprone.annotations.Keep; import com.google.errorprone.annotations.RestrictedApi; +import jakarta.annotation.PostConstruct; +import jakarta.validation.Valid; +import jakarta.validation.ValidatorFactory; +import jakarta.validation.constraints.AssertFalse; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import uk.ac.manchester.spinnaker.alloc.ForTestingOnly; import uk.ac.manchester.spinnaker.alloc.db.DatabaseAPI.Connection; import uk.ac.manchester.spinnaker.alloc.db.DatabaseAPI.Update; @@ -242,8 +242,9 @@ private static boolean badName(String name) { @JsonIgnore @AssertTrue(message = "all boards must have sane logical coordinates") private boolean isCoordinateSane() { - return boardLocations.keySet().stream().allMatch(loc -> (loc.x >= 0) - && (loc.x < width) && (loc.y >= 0) && (loc.y < height)); + return boardLocations.keySet().stream() + .allMatch(loc -> (loc.x() >= 0) && (loc.x() < width) + && (loc.y() >= 0) && (loc.y() < height)); } @Keep @@ -313,9 +314,9 @@ private static int limit(int value, int limit) { * @return The new location */ private TriadCoords move(TriadCoords here, Direction direction) { - var di = DirInfo.get(here.z, direction); - return new TriadCoords(limit(here.x + di.dx, width), - limit(here.y + di.dy, height), here.z + di.dz); + var di = DirInfo.get(here.z(), direction); + return new TriadCoords(limit(here.x() + di.dx, width), + limit(here.y() + di.dy, height), here.z() + di.dz); } @JsonPOJOBuilder @@ -414,88 +415,36 @@ public Machine build() { } /** - * A configuration description. JSON-deserializable (the only supported - * mechanism for generating an instance). Largely ignored as it represents - * configuration settings that we handle elsewhere. However, the + * A configuration description. JSON-deserializable (the only actually + * supported mechanism for generating an instance). Largely ignored as it + * represents configuration settings that we handle elsewhere. However, the * {@code machines} property is interesting. * * @author Donal Fellows + * @param machines + * The machines to manage. + * @param port + * The port for the service to listen on. (Ignored) + * @param ip + * The host address for the service to listen on. Empty = all + * interfaces. (Ignored) + * @param timeoutCheckInterval + * How often (in seconds) to check for timeouts. (Ignored) + * @param maxRetiredJobs + * How many retired jobs to retain. (Ignored) + * @param secondsBeforeFree + * Time to wait before freeing. (Ignored) */ - public static final class Configuration { - private @NotNull List<@Valid Machine> machines; - - @TCPPort - private int port; - - @IPAddress(nullOK = true, emptyOK = true) - private String ip; - - @Positive - private double timeoutCheckInterval; - - @Positive - private int maxRetiredJobs; - - @Positive - private int secondsBeforeFree; - - /** @return The machines to manage. */ - public List getMachines() { - return machines; - } - - void setMachines(List machines) { - this.machines = copy(machines); - } - - /** @return The port for the service to listen on. (Ignored) */ - public int getPort() { - return port; - } - - void setPort(int port) { - this.port = port; - } - - /** - * @return The host address for the service to listen on. Empty = all - * interfaces. (Ignored) - */ - public String getIp() { - return ip; - } - - void setIp(String ip) { - this.ip = ip; - } - - /** @return How often (in seconds) to check for timeouts. (Ignored) */ - public double getTimeoutCheckInterval() { - return timeoutCheckInterval; - } - - void setTimeoutCheckInterval(double timeoutCheckInterval) { - this.timeoutCheckInterval = timeoutCheckInterval; - } - - /** @return How many retired jobs to retain. (Ignored) */ - public int getMaxRetiredJobs() { - return maxRetiredJobs; - } - - void setMaxRetiredJobs(int maxRetiredJobs) { - this.maxRetiredJobs = maxRetiredJobs; - } - - /** @return Time to wait before freeing. (Ignored) */ - public int getSecondsBeforeFree() { - return secondsBeforeFree; - } - - void setSecondsBeforeFree(int secondsBeforeFree) { - this.secondsBeforeFree = secondsBeforeFree; - } - + public record Configuration(// + @JsonProperty("machines") @NotNull @Valid List machines, + @JsonProperty("port") @TCPPort int port, + @JsonProperty("ip") @IPAddress(nullOK = true, emptyOK = true) // + String ip, // + @JsonProperty("timeout-check-interval") @Positive // + double timeoutCheckInterval, + @JsonProperty("max-retired-jobs") @Positive int maxRetiredJobs, + @JsonProperty("seconds-before-free") @Positive // + double secondsBeforeFree) { @Override public String toString() { return new StringBuilder("Configuration(").append("machines=") @@ -541,7 +490,7 @@ public List readMachineDefinitions(File file) throws IOException, JsonParseException, JsonMappingException { var cfg = mapper.readValue(file, Configuration.class); validate(cfg); - return cfg.getMachines(); + return cfg.machines(); } /** @@ -563,7 +512,7 @@ public List readMachineDefinitions(InputStream stream) throws IOException, JsonParseException, JsonMappingException { var cfg = mapper.readValue(stream, Configuration.class); validate(cfg); - return cfg.getMachines(); + return cfg.machines(); } /** @@ -654,7 +603,7 @@ public void loadMachineDefinitions(InputStream stream) */ public void loadMachineDefinitions(Configuration configuration) { try (var sql = new Updates()) { - for (var machine : configuration.getMachines()) { + for (var machine : configuration.machines()) { sql.transaction(() -> loadMachineDefinition(sql, machine)); } } @@ -678,6 +627,7 @@ public void loadMachineDefinition(Machine machine) { * @author Donal Fellows */ public static class InsertFailedException extends RuntimeException { + @Serial private static final long serialVersionUID = -4930512416142843777L; InsertFailedException(String table) { @@ -719,7 +669,7 @@ private Map makeBMPs(Updates sql, Machine machine, int machineId) { var bmpIds = new HashMap(); machine.bmpIPs.forEach((bmp, ip) -> sql.makeBMP - .key(machineId, ip, bmp.cabinet, bmp.frame) + .key(machineId, ip, bmp.cabinet(), bmp.frame()) .ifPresent(id -> bmpIds.put(bmp, id))); return bmpIds; } @@ -737,12 +687,12 @@ private Map makeBoards(Updates sql, Machine machine, machine.deadBoards.contains(triad) ? "dead" : "live", triad); sql.makeBoard - .key(machineId, addr, bmpID, phys.b, triad.x, triad.y, - triad.z, root.getX(), root.getY(), + .key(machineId, addr, bmpID, phys.b(), triad.x(), triad.y(), + triad.z(), root.getX(), root.getY(), !machine.deadBoards.contains(triad)) .ifPresent(id -> boardIds.put(triad, id)); - maxX = max(maxX, triad.x * TRIAD_CHIP_SIZE); - maxY = max(maxY, triad.y * TRIAD_CHIP_SIZE); + maxX = max(maxX, triad.x() * TRIAD_CHIP_SIZE); + maxY = max(maxY, triad.y() * TRIAD_CHIP_SIZE); } /* * Note that even in single-board setups, the max coordinates are as if diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/MachineStateControl.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/MachineStateControl.java index 2fff1a718a..b711f9ed6c 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/MachineStateControl.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/MachineStateControl.java @@ -28,6 +28,7 @@ import static uk.ac.manchester.spinnaker.utils.CollectionUtils.batch; import static uk.ac.manchester.spinnaker.utils.CollectionUtils.lmap; +import java.io.Serial; import java.time.Instant; import java.util.HashSet; import java.util.List; @@ -38,12 +39,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.function.Function; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; @@ -54,6 +49,11 @@ import com.google.errorprone.annotations.CompileTimeConstant; import com.google.errorprone.annotations.MustBeClosed; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.alloc.SpallocProperties; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.StateControlProperties; import uk.ac.manchester.spinnaker.alloc.allocator.Epochs; @@ -111,10 +111,13 @@ public class MachineStateControl extends DatabaseAwareBean { private void launchBackground() { props = properties.getStateControl(); // After a minute, start retrieving board serial numbers - readAllTask = - executor.schedule((Runnable) this::readAllBoardSerialNumbers, - Instant.now().plus(props.getBlacklistTimeout())); - // Why can't I pass a Duration directly there? + // Don't do this if testing + if (!properties.getTransceiver().isDummy()) { + readAllTask = executor.schedule( + (Runnable) this::readAllBoardSerialNumbers, + Instant.now().plus(props.getBlacklistTimeout())); + // Why can't I pass a Duration directly there? + } } @PreDestroy @@ -327,8 +330,8 @@ public Optional findTriad(@NotBlank String machine, @Valid @NonNull TriadCoords coords) { return executeRead(conn -> { try (var q = conn.query(FIND_BOARD_BY_NAME_AND_XYZ)) { - return q.call1(BoardState::new, machine, coords.x, coords.y, - coords.z); + return q.call1(BoardState::new, machine, coords.x(), coords.y(), + coords.z()); } }); } @@ -346,8 +349,8 @@ public Optional findPhysical(@NotBlank String machine, @Valid @NotNull PhysicalCoords coords) { return executeRead(conn -> { try (var q = conn.query(FIND_BOARD_BY_NAME_AND_CFB)) { - return q.call1(BoardState::new, machine, coords.c, coords.f, - coords.b); + return q.call1(BoardState::new, machine, coords.c(), coords.f(), + coords.b()); } }); } @@ -386,28 +389,23 @@ public List getMachineTagging() { }); } - private class MachineNameId { - int id; - - String name; - - MachineNameId(Row row) { - id = row.getInt("machine_id"); - name = row.getString("machine_name"); - } - } - /** * @return The unacknowledged reports about boards with potential problems * in existing machines, categorised by machine. */ public Map> getMachineReports() { + record MachineNameId(int id, String name) { + MachineNameId(Row row) { + this(row.getInt("machine_id"), row.getString("machine_name")); + } + } + return executeRead(conn -> { try (var getMachines = conn.query(GET_ALL_MACHINES); var getMachineReports = conn.query(GET_MACHINE_REPORTS)) { return Row.stream(getMachines.call(MachineNameId::new, true)) - .toMap(m -> m.name, m -> getMachineReports.call( - BoardIssueReport::new, m.id)); + .toMap(MachineNameId::name, m -> getMachineReports + .call(BoardIssueReport::new, m.id())); } }); } @@ -466,6 +464,7 @@ public void setMachineState(@NotBlank String machineName, * @author Donal Fellows */ public static final class MachineStateException extends RuntimeException { + @Serial private static final long serialVersionUID = -6450838951059318431L; private MachineStateException(String msg, Exception exn) { @@ -566,7 +565,7 @@ private void readAllBoardSerialNumbers() { private void scheduleSerialNumberReads(String machineName) { batchReqs(machineName, "retrieving serial numbers", props.getSerialReadBatchSize(), - id -> new Op(CREATE_SERIAL_READ_REQ, id), Op::completed); + id -> new BmpOp(CREATE_SERIAL_READ_REQ, id), BmpOp::completed); } private interface InterruptableConsumer { @@ -600,8 +599,8 @@ private interface InterruptableConsumer { * How to process the results of an individual operation. */ private void batchReqs(String machineName, String action, int batchSize, - Function opGenerator, - InterruptableConsumer opResultsHandler) { + Function opGenerator, + InterruptableConsumer opResultsHandler) { var boards = executeRead(c -> listAllBoards(c, machineName)); for (var batch : batch(batchSize, boards)) { /* @@ -626,7 +625,7 @@ private void batchReqs(String machineName, String action, int batchSize, } } } finally { - ops.forEach(Op::close); + ops.forEach(BmpOp::close); } if (stop) { // Mark as interrupted @@ -646,7 +645,7 @@ private void batchReqs(String machineName, String action, int batchSize, public void updateAllBlacklists(@NotBlank String machineName) { batchReqs(requireNonNull(machineName), "retrieving blacklists", props.getBlacklistReadBatchSize(), - id -> new Op(CREATE_BLACKLIST_READ, id), + id -> new BmpOp(CREATE_BLACKLIST_READ, id), op -> op.getResult(serial("data", Blacklist.class)) .ifPresent(bl -> { blacklistStore.writeBlacklist(op.boardId, bl); @@ -719,7 +718,7 @@ public void writeBlacklistToDB(int boardId, */ public Optional readBlacklistFromMachine(int boardId, int bmpId) throws InterruptedException { - try (var op = new Op(CREATE_BLACKLIST_READ, boardId)) { + try (var op = new BmpOp(CREATE_BLACKLIST_READ, boardId)) { bmpController.triggerSearch(List.of(bmpId)); return op.getResult(serial("data", Blacklist.class)); } @@ -744,7 +743,7 @@ public Optional readBlacklistFromMachine(int boardId, int bmpId) */ public void writeBlacklistToMachine(int boardId, int bmpId, @Valid Blacklist blacklist) throws InterruptedException { - try (var op = new Op(CREATE_BLACKLIST_WRITE, boardId, blacklist)) { + try (var op = new BmpOp(CREATE_BLACKLIST_WRITE, boardId, blacklist)) { bmpController.triggerSearch(List.of(bmpId)); op.completed(); } @@ -765,7 +764,7 @@ public void writeBlacklistToMachine(int boardId, int bmpId, */ public String getSerialNumber(BoardState board) throws InterruptedException { - try (var op = new Op(CREATE_SERIAL_READ_REQ, board.id)) { + try (var op = new BmpOp(CREATE_SERIAL_READ_REQ, board.id)) { bmpController.triggerSearch(List.of(board.bmpId)); op.completed(); } @@ -788,7 +787,7 @@ public String getSerialNumber(BoardState board) */ public Optional readTemperatureFromMachine(int boardId) throws InterruptedException { - try (var op = new Op(CREATE_TEMP_READ_REQ, boardId)) { + try (var op = new BmpOp(CREATE_TEMP_READ_REQ, boardId)) { return op.getResult(serial("data", ADCInfo.class)); } } @@ -816,7 +815,7 @@ public boolean isBlacklistSynched(BoardState board) { * * @author Donal Fellows */ - private final class Op implements AutoCloseable { + private final class BmpOp implements AutoCloseable { private final int op; private final Epoch epoch; @@ -833,7 +832,8 @@ private final class Op implements AutoCloseable { * will be the board that the operation refers to. */ @MustBeClosed - Op(@CompileTimeConstant final String operation, Object... args) { + @SuppressWarnings("CompileTimeConstant") + BmpOp(@CompileTimeConstant final String operation, Object... args) { boardId = (Integer) args[0]; var e = epochs.getBlacklistEpoch(boardId); op = execute(conn -> { diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/ReportMailSender.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/ReportMailSender.java index b08cd81f32..b9b1c21b83 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/ReportMailSender.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/ReportMailSender.java @@ -19,8 +19,6 @@ import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.beans.factory.config.BeanDefinition.ROLE_SUPPORT; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Role; @@ -29,6 +27,7 @@ import org.springframework.mail.javamail.JavaMailSender; import org.springframework.stereotype.Component; +import jakarta.annotation.PostConstruct; import uk.ac.manchester.spinnaker.alloc.SpallocProperties; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.ReportProperties; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/UserControl.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/UserControl.java index 45a494886b..9ebbf31a54 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/UserControl.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/admin/UserControl.java @@ -85,7 +85,8 @@ List allUsers(boolean internal) { } @UsedInJavadocOnly(SQLQueries.class) - private class UserCheckSQL extends AbstractSQL { + private sealed class UserCheckSQL extends AbstractSQL + permits CreateSQL, DeleteUserSQL, UpdateAllSQL, UpdatePassSQL { private final Query userCheck = conn.query(GET_USER_ID); /** See {@link SQLQueries#GET_USER_DETAILS}. */ @@ -300,7 +301,7 @@ private static MemberRecord member(Row row) { */ public List listUsers() { try (var sql = new AllUsersSQL()) { - return sql.transactionRead(() -> sql.allUsers()); + return sql.transactionRead(sql::allUsers); } } @@ -596,24 +597,6 @@ public PasswordChangeRecord updateUser(Principal principal, } } - /** - * Just a tuple extracted from a row. Only used in - * {@link #updateUser(Principal,PasswordChangeRecord,UpdatePassSQL)}; it's - * only not a local class to work around JDK-8144673 (fixed - * by Java 11). - */ - private static class GetUserResult { - final PasswordChangeRecord baseUser; - - final String oldEncPass; - - GetUserResult(Row row) { - baseUser = passChange(row); - oldEncPass = row.getString("encrypted_password"); - } - } - /** * Back end of {@link #updateUser(Principal,PasswordChangeRecord)}. *

@@ -626,10 +609,28 @@ private static class GetUserResult { * What to update * @param sql * How to touch the DB - * @return What was updated + * @return What was updated, without the actual password fields + * filled out. */ private PasswordChangeRecord updateUser(Principal principal, PasswordChangeRecord user, UpdatePassSQL sql) { + /** + * Record extracted from a row of the {@code user_info} table. + * + * @param baseUser + * The user's password change record, without the + * actual password fields filled out. + * @param oldEncPass + * Old encoded password. + * @see UserControl#updateUser(Principal, PasswordChangeRecord, + * UpdatePassSQL) + */ + record GetUserResult(PasswordChangeRecord baseUser, String oldEncPass) { + private GetUserResult(Row row) { + this(passChange(row), row.getString("encrypted_password")); + } + } + var result = sql .transaction(() -> sql.getPasswordedUser .call1(GetUserResult::new, principal.getName())) @@ -641,7 +642,7 @@ private PasswordChangeRecord updateUser(Principal principal, // This is a SLOW operation; must not hold transaction here if (!passServices.matchPassword(user.getOldPassword(), - result.oldEncPass)) { + result.oldEncPass())) { throw new BadCredentialsException("bad password"); } @@ -652,11 +653,11 @@ private PasswordChangeRecord updateUser(Principal principal, var newEncPass = passServices.encodePassword(user.getNewPassword()); return sql.transaction(() -> { if (sql.setPassword.call(newEncPass, - result.baseUser.getUserId()) != 1) { + result.baseUser().getUserId()) != 1) { throw new InternalAuthenticationServiceException( "failed to update database"); } - return result.baseUser; + return result.baseUser(); }); } @@ -667,7 +668,7 @@ private PasswordChangeRecord updateUser(Principal principal, */ public List listGroups() { try (var sql = new GroupsSQL()) { - return sql.transactionRead(() -> sql.listGroups()); + return sql.transactionRead(sql::listGroups); } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/AllocatorTask.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/AllocatorTask.java index 416907d280..ca9234488b 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/AllocatorTask.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/AllocatorTask.java @@ -44,7 +44,7 @@ import java.util.Set; import java.util.stream.Stream; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -131,6 +131,13 @@ public class AllocatorTask extends DatabaseAwareBean // instead set by setter in postconstruct of BMPController private BMPController bmpController; + /** + * Only called by BMP controller. + * + * @param bmpController + * The BMP controller + * @hidden + */ public void setBMPController(BMPController bmpController) { this.bmpController = bmpController; } @@ -138,10 +145,10 @@ public void setBMPController(BMPController bmpController) { @PostConstruct @SuppressWarnings("FutureReturnValueIgnored") private void init() { - scheduler.scheduleAtFixedRate(() -> allocate(), allocProps.getPeriod()); - scheduler.scheduleAtFixedRate(() -> expireJobs(), + scheduler.scheduleAtFixedRate(this::allocate, allocProps.getPeriod()); + scheduler.scheduleAtFixedRate(this::expireJobs, keepAliveProps.getExpiryPeriod()); - scheduler.schedule(() -> tombstone(), + scheduler.schedule(this::tombstone, new CronTrigger(historyProps.getSchedule())); } @@ -215,21 +222,14 @@ private boolean update(int jobId, JobState sourceState, * Helper class representing a rectangle of triads. * * @author Donal Fellows + * @param width + * Width of rectangle, in triads. + * @param height + * Height of rectangle, in triads. + * @param depth + * Depth of rectangle. 1 or 3 */ - private static final class Rectangle { - final int width; - - final int height; - - /** Depth of rectangle. 1 or 3 */ - final int depth; - - private Rectangle(int width, int height, int depth) { - this.width = width; - this.height = height; - this.depth = depth; - } - + private record Rectangle(int width, int height, int depth) { private Rectangle(Row row) { this(row.getInt("max_width"), row.getInt("max_height"), TRIAD_DEPTH); @@ -274,19 +274,9 @@ public synchronized void allocate() { } } - private class Perimeter { - int boardId; - - Direction direction; - - Perimeter(Row row) { - boardId = row.getInt("board_id"); - direction = row.getEnum("direction", Direction.class); - } - } - /** Encapsulates the queries and updates used in power control. */ - private class PowerSQL extends AbstractSQL { + private sealed class PowerSQL extends AbstractSQL + permits AllocSQL, DestroySQL { /** Get basic information about a specific job. */ private final Query getJobState; @@ -315,7 +305,7 @@ private class PowerSQL extends AbstractSQL { getJobState = conn.query(GET_JOB); getJobBoards = conn.query(GET_JOB_BOARDS); getPerimeter = conn.query(getPerimeterLinks); - issuePowerChange = conn.update(issueChangeForJob); + issuePowerChange = conn.update(ISSUE_CHANGE_FOR_JOB); setStatePending = conn.update(SET_STATE_PENDING); setStateDestroyed = conn.update(SET_STATE_DESTROYED); } @@ -391,13 +381,13 @@ private final class AllocSQL extends PowerSQL { AllocSQL(Connection conn) { super(conn); bumpImportance = conn.update(BUMP_IMPORTANCE); - getTasks = conn.query(getAllocationTasks); + getTasks = conn.query(GET_ALLOCATION_TASKS); delete = conn.update(DELETE_TASK); findFreeBoard = conn.query(FIND_FREE_BOARD); getRectangles = conn.query(findRectangle); getRectangleAt = conn.query(findRectangleAt); countConnectedBoards = conn.query(countConnected); - findSpecificBoard = conn.query(findLocation); + findSpecificBoard = conn.query(FIND_LOCATION); getConnectedBoardIDs = conn.query(getConnectedBoards); allocBoard = conn.update(ALLOCATE_BOARDS_BOARD); allocJob = conn.update(ALLOCATE_BOARDS_JOB); @@ -448,6 +438,7 @@ public void close() { } } + /** Encapsulates the task to do a particular allocation. */ private class AllocTask { final int id; @@ -531,15 +522,14 @@ Collection allocate(AllocSQL sql) { * A set of information about the allocations that have been made. */ class Allocations { - /** The BMPs that have been affected by the allocations. **/ final Set bmps = new HashSet<>(); /** The Machines that have been affected by the allocations. **/ - final Set machines = new HashSet<>(); + private final Set machines = new HashSet<>(); /** The jobs that have been affected by the allocations. **/ - final List jobIds = new ArrayList<>(); + private final List jobIds = new ArrayList<>(); Allocations() { // Does nothing @@ -595,7 +585,7 @@ private Allocations allocate(Connection conn) { int maxImportance = -1; log.trace("Allocate running"); var allocations = new Allocations(); - for (AllocTask task : sql.getTasks.call(AllocTask::new, QUEUED)) { + for (var task : sql.getTasks.call(AllocTask::new, QUEUED)) { if (task.importance > maxImportance) { maxImportance = task.importance; } else if (task.importance < maxImportance @@ -695,22 +685,18 @@ public void tombstone() { /** * Describes what the first stage of the tombstoner has copied. + * + * @param jobs + * The jobs that were copied. + * @param allocs + * The allocations that were copied. */ - static final class Copied { - private final List jobs; - - private final List allocs; - - private Copied(List jobs, List allocs) { - this.jobs = jobs; - this.allocs = allocs; - } - - private Stream allocs() { + record Copied(List jobs, List allocs) { + private Stream allocStream() { return allocs.stream().filter(Objects::nonNull); } - private Stream jobs() { + private Stream jobStream() { return jobs.stream().filter(Objects::nonNull); } @@ -729,100 +715,113 @@ int numJobs() { int numAllocs() { return allocs.size(); } - } - - private class HistoricalAlloc { - int allocId; - int jobId; - - int boardId; - - Instant allocTimestamp; - - HistoricalAlloc(Row row) { - allocId = row.getInt("alloc_id"); - jobId = row.getInt("job_id"); - boardId = row.getInt("board_id"); - allocTimestamp = row.getInstant("alloc_timestamp"); - } + /** + * Details of a copied allocation record. + * + * @param allocId + * Allocation ID + * @param jobId + * Job ID + * @param boardId + * Board ID (the board that was allocated) + * @param allocTimestamp + * When the board was allocated. + */ + record HistoricalAlloc(int allocId, int jobId, int boardId, + Instant allocTimestamp) { + HistoricalAlloc(Row row) { + this(row.getInt("alloc_id"), row.getInt("job_id"), + row.getInt("board_id"), + row.getInstant("alloc_timestamp")); + } - Object[] args() { - return new Object[] { - allocId, jobId, boardId, allocTimestamp - }; + private Object[] args() { + return new Object[] { + allocId, jobId, boardId, allocTimestamp + }; + } } - } - - private class HistoricalJob { - int jobId; - - int machineId; - - String owner; - - Instant createTimestamp; - - int width; - - int height; - - int depth; - - int allocatedRoot; - - Instant keepaliveInterval; - - String keepaliveHost; - - String deathReason; - - Instant deathTimestamp; - - byte[] originalRequest; - - Instant allocationTimestamp; - int allocationSize; - - String machineName; - - String userName; - - int groupId; - - String groupName; - - HistoricalJob(Row row) { - jobId = row.getInt("job_id"); - machineId = row.getInt("machine_id"); - owner = row.getString("owner"); - createTimestamp = row.getInstant("create_timestamp"); - width = row.getInt("width"); - height = row.getInt("height"); - depth = row.getInt("depth"); - allocatedRoot = row.getInt("allocated_root"); - keepaliveInterval = row.getInstant("keepalive_interval"); - keepaliveHost = row.getString("keepalive_host"); - deathReason = row.getString("death_reason"); - deathTimestamp = row.getInstant("death_timestamp"); - originalRequest = row.getBytes("original_request"); - allocationTimestamp = row.getInstant("allocation_timestamp"); - allocationSize = row.getInt("allocation_size"); - machineName = row.getString("machine_name"); - userName = row.getString("user_name"); - groupId = row.getInt("group_id"); - groupName = row.getString("group_name"); - } + /** + * Details of a copied job record. + * + * @param jobId + * Job ID + * @param machineId + * Machine ID + * @param owner + * Whose job was it (user ID) + * @param createTimestamp + * When the job was submitted + * @param width + * Width of requested allocation, in triads + * @param height + * Height of requested allocation, in triads + * @param depth + * Depth of requested allocation; 1 (single board) or 3 + * @param allocatedRoot + * ID of board at root of allocation + * @param keepaliveInterval + * How often keep-alive messages should come + * @param keepaliveHost + * IP address of machine keeping job alive + * @param deathReason + * Why did the job terminate? + * @param deathTimestamp + * When did the job terminate + * @param originalRequest + * What was actually asked for. (Original request data) + * @param allocationTimestamp + * When did we complete allocation. Quota consumption was + * from this moment to the death timestamp. + * @param allocationSize + * How many boards were allocated + * @param machineName + * Name of allocated machine (convenience; implied by machine + * ID) + * @param userName + * Name of user (convenience; implied by owner ID) + * @param groupId + * Group for accounting purposes + * @param groupName + * Name of group (convenience; implied by group ID) + */ + record HistoricalJob(int jobId, int machineId, String owner, + Instant createTimestamp, int width, int height, int depth, + int allocatedRoot, Instant keepaliveInterval, + String keepaliveHost, String deathReason, + Instant deathTimestamp, byte[] originalRequest, + Instant allocationTimestamp, int allocationSize, + String machineName, String userName, int groupId, + String groupName) { + HistoricalJob(Row row) { + this(row.getInt("job_id"), row.getInt("machine_id"), + row.getString("owner"), + row.getInstant("create_timestamp"), row.getInt("width"), + row.getInt("height"), row.getInt("depth"), + row.getInt("allocated_root"), + row.getInstant("keepalive_interval"), + row.getString("keepalive_host"), + row.getString("death_reason"), + row.getInstant("death_timestamp"), + row.getBytes("original_request"), + row.getInstant("allocation_timestamp"), + row.getInt("allocation_size"), + row.getString("machine_name"), + row.getString("user_name"), row.getInt("group_id"), + row.getString("group_name")); + } - Object[] args() { - return new Object[] { - jobId, machineId, owner, createTimestamp, - width, height, depth, allocatedRoot, keepaliveInterval, - keepaliveHost, deathReason, deathTimestamp, originalRequest, - allocationTimestamp, allocationSize, machineName, userName, - groupId, groupName - }; + private Object[] args() { + return new Object[] { + jobId, machineId, owner, createTimestamp, width, height, + depth, allocatedRoot, keepaliveInterval, keepaliveHost, + deathReason, deathTimestamp, originalRequest, + allocationTimestamp, allocationSize, machineName, userName, + groupId, groupName + }; + } } } @@ -849,16 +848,17 @@ private Copied tombstone(Connection conn, Connection histConn) { var writeJobs = histConn.update(WRITE_HISTORICAL_JOBS); var writeAllocs = histConn.update(WRITE_HISTORICAL_ALLOCS)) { var grace = historyProps.getGracePeriod(); - var copied = conn.transaction( - () -> new Copied(readJobs.call(HistoricalJob::new, grace), - readAllocs.call(HistoricalAlloc::new, grace))); + var copied = conn.transaction(() -> new Copied( + readJobs.call(Copied.HistoricalJob::new, grace), + readAllocs.call(Copied.HistoricalAlloc::new, grace))); histConn.transaction(() -> { - copied.allocs().forEach((a) -> writeAllocs.call(a.args())); - copied.jobs().forEach((j) -> writeJobs.call(j.args())); + copied.allocStream().forEach(a -> writeAllocs.call(a.args())); + copied.jobStream().forEach(j -> writeJobs.call(j.args())); }); conn.transaction(() -> { - copied.allocs().forEach((a) -> deleteAllocs.call(a.allocId)); - copied.jobs().forEach((j) -> deleteJobs.call(j.jobId)); + copied.allocStream() + .forEach(a -> deleteAllocs.call(a.allocId())); + copied.jobStream().forEach(j -> deleteJobs.call(j.jobId())); }); return copied; } @@ -905,7 +905,9 @@ private Collection destroyJob(Connection conn, int id, var bmps = setPower(sql, id, OFF, DESTROYED); sql.killAlloc.call(id); sql.markAsDestroyed.call(reason, id); - log.info("job {} marked as destroyed", id); + JobLifecycle.log.info( + "destroyed job {}; reclaiming boards in {} frames", id, + bmps.size()); return bmps; } finally { quotaManager.finishJob(id); @@ -1060,7 +1062,7 @@ private Collection allocateDimensions(AllocSQL sql, private int connectedSize(AllocSQL sql, int machineId, TriadCoords root, int width, int height) { return sql.countConnectedBoards - .call1(integer("connected_size"), machineId, root.x, root.y, + .call1(integer("connected_size"), machineId, root.x(), root.y(), width, height).orElse(-1); } @@ -1152,10 +1154,11 @@ private Collection allocateTriadsAt(AllocSQL sql, int jobId, private Collection setAllocation(AllocSQL sql, int jobId, Rectangle rect, int machineId, TriadCoords root) { log.debug("performing allocation for {}: {}x{}x{} at {}:{}:{}", jobId, - rect.width, rect.height, rect.depth, root.x, root.y, root.z); - var boardsToAllocate = sql.getConnectedBoardIDs - .call(integer("board_id"), machineId, root.x, root.y, root.z, - rect.width, rect.height, rect.depth); + rect.width, rect.height, rect.depth, root.x(), root.y(), + root.z()); + var boardsToAllocate = sql.getConnectedBoardIDs.call( + integer("board_id"), machineId, root.x(), root.y(), root.z(), + rect.width, rect.height, rect.depth); if (boardsToAllocate.isEmpty()) { log.debug("No boards to allocate"); return List.of(); @@ -1233,6 +1236,13 @@ private Collection setPower(PowerSQL sql, int jobId, // Number of changes pending, one per board int numPending = 0; + record Perimeter(int boardId, Direction direction) { + Perimeter(Row row) { + this(row.getInt("board_id"), + row.getEnum("direction", Direction.class)); + } + } + var bmps = new HashSet(); if (power == ON) { /* @@ -1241,10 +1251,10 @@ private Collection setPower(PowerSQL sql, int jobId, * switched off because they are links to boards that are not * allocated to the job. Off-board links are shut off by default. */ - var perimeterLinks = Row.stream( - sql.getPerimeter.call(Perimeter::new, jobId)) - .toCollectingMap(Direction.class, (p) -> p.boardId, - (p) -> p.direction); + var perimeterLinks = + Row.stream(sql.getPerimeter.call(Perimeter::new, jobId)) + .toCollectingMap(Direction.class, + Perimeter::boardId, Perimeter::direction); for (var board : boards) { var toChange = perimeterLinks.getOrDefault(board.boardId, diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/Epochs.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/Epochs.java index 5d587f3c2d..6d19209cb5 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/Epochs.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/Epochs.java @@ -20,26 +20,21 @@ import java.time.Duration; import java.util.Collection; -import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; -import java.util.function.BiConsumer; - -import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.TaskScheduler; import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; import com.google.errorprone.annotations.concurrent.GuardedBy; -import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; - /** * Manages waiting for values. * @@ -195,7 +190,7 @@ public void blacklistChanged(int board) { * @author Donal Fellows * @author Andrew Rowley */ - public final class Epoch { + public static final class Epoch { private final EpochMap map; private final List ids; @@ -288,10 +283,8 @@ synchronized boolean checkEmptyValues() { void changed(int id) { var items = getSet(id); if (nonNull(items)) { - synchronized (items) { - for (var item : items) { - item.updateChanged(id); - } + for (var item : items) { + item.updateChanged(id); } } } @@ -303,9 +296,6 @@ void changed(int id) { * The key into the map. * @return The removed set of epochs. Empty if the key is absent. */ - @UsedInJavadocOnly({ - BiConsumer.class, ConcurrentModificationException.class - }) private synchronized Set getSet(Integer id) { var weakmap = map.get(id); if (weakmap == null) { @@ -318,7 +308,7 @@ private synchronized Set getSet(Integer id) { synchronized void addAll(Epochs.Epoch epoch, List ids) { for (var id : ids) { - map.computeIfAbsent(id, key -> new WeakHashMap<>()).put(epoch, OBJ); + map.computeIfAbsent(id, __ -> new WeakHashMap<>()).put(epoch, OBJ); } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/ProxyRememberer.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/ProxyRememberer.java index 298a56e79a..4d9c3cc01c 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/ProxyRememberer.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/ProxyRememberer.java @@ -22,12 +22,11 @@ import java.util.List; import java.util.Map; -import javax.annotation.PreDestroy; - import org.springframework.stereotype.Component; import com.google.errorprone.annotations.concurrent.GuardedBy; +import jakarta.annotation.PreDestroy; import uk.ac.manchester.spinnaker.alloc.proxy.ProxyCore; /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/QuotaManager.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/QuotaManager.java index e5cd3af4d8..cdfc2f9b7a 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/QuotaManager.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/QuotaManager.java @@ -27,8 +27,8 @@ import java.util.List; import java.util.Optional; -import javax.annotation.PostConstruct; -import javax.ws.rs.BadRequestException; +import jakarta.annotation.PostConstruct; +import jakarta.ws.rs.BadRequestException; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -210,25 +210,14 @@ public Optional addQuota(int groupId, int delta) { * operation. * * @author Donal Fellows + * @param name + * The name of the group. + * @param quota + * The new quota of the group. */ - public static final class AdjustedQuota { - private final String name; - - private final Long quota; - + public record AdjustedQuota(String name, Long quota) { private AdjustedQuota(Row row) { - this.name = row.getString("group_name"); - this.quota = row.getLong("quota"); - } - - /** @return The name of the group. */ - public String getName() { - return name; - } - - /** @return The new quota of the group. */ - public Long getQuota() { - return quota; + this(row.getString("group_name"), row.getLong("quota")); } } @@ -240,6 +229,7 @@ private final class AdjustQuotaSQL extends AbstractSQL { @Override public void close() { adjustQuota.close(); + getQuota.close(); super.close(); } @@ -278,7 +268,7 @@ private void doConsolidate(Connection c) { } } - private class ConsolidateSQL extends AbstractSQL { + private final class ConsolidateSQL extends AbstractSQL { private final Query getConsoldationTargets = conn.query(GET_CONSOLIDATION_TARGETS); @@ -322,17 +312,13 @@ private class Target { } } - private static class QuotaInfo { - /** The size of quota remaining, in board-seconds. */ - final long quota; - - /** The units that the quota was measured in on the NMPI. */ - final String units; - - QuotaInfo(long quota, String units) { - this.quota = quota; - this.units = units; - } + /** + * @param quota + * The size of quota remaining, in board-seconds. + * @param units + * The units that the quota was measured in on the NMPI. + */ + private record QuotaInfo(long quota, String units) { } private QuotaInfo parseQuotaData(List projects) { @@ -371,16 +357,16 @@ final Optional mayCreateNMPISession(String collab) { var projects = nmpi.getProjects(STATUS_ACCEPTED, collab); var info = parseQuotaData(projects); - log.debug("Setting quota of collab {} to {}", collab, info.quota); + log.debug("Setting quota of collab {} to {}", collab, info.quota()); // Update quota in group for collab from NMPI try (var c = getConnection(); var setQuota = c.update(SET_COLLAB_QUOTA)) { - c.transaction(() -> setQuota.call(info.quota, collab)); + c.transaction(() -> setQuota.call(info.quota(), collab)); } - if (info.quota > 0) { - return Optional.of(info.units); + if (info.quota() > 0) { + return Optional.of(info.units()); } return Optional.empty(); } @@ -489,17 +475,13 @@ final Optional mayUseNMPIJob(String user, new NMPIJobQuotaDetails(job.getCollab(), quotaUnits.get())); } - static final class NMPIJobQuotaDetails { - /** The collaboratory ID. */ - final String collabId; - - /** The units of the Quota. */ - final String quotaUnits; - - private NMPIJobQuotaDetails(String collabId, String quotaUnits) { - this.collabId = collabId; - this.quotaUnits = quotaUnits; - } + /** + * @param collabId + * The collaboratory ID. + * @param quotaUnits + * The units of the Quota. + */ + record NMPIJobQuotaDetails(String collabId, String quotaUnits) { } void associateNMPIJob(int jobId, int nmpiJobId, String quotaUnits) { @@ -511,12 +493,8 @@ void associateNMPIJob(int jobId, int nmpiJobId, String quotaUnits) { } /** Results of database queries. */ - private static final class FinishInfo { - Optional quota; - - Optional session; - - Optional job; + private record FinishInfo(Optional quota, Optional session, + Optional job) { } private FinishInfo getFinishingInfo(int jobId) { @@ -525,13 +503,11 @@ private FinishInfo getFinishingInfo(int jobId) { var getNMPIJob = c.query(GET_JOB_NMPI_JOB); var getUsage = c.query(GET_JOB_USAGE_AND_QUOTA)) { // Get the quota used - return c.transaction(false, () -> { - var i = new FinishInfo(); - i.quota = getUsage.call1(r -> r.getLong("quota_used"), jobId); - i.session = getSession.call1(Session::new, jobId); - i.job = getNMPIJob.call1(NMPIJob::new, jobId); - return i; - }); + return c.transaction(false, + () -> new FinishInfo( + getUsage.call1(r -> r.getLong("quota_used"), jobId), + getSession.call1(Session::new, jobId), + getNMPIJob.call1(NMPIJob::new, jobId))); } } @@ -541,16 +517,17 @@ final void finishJob(int jobId) { // From here on, we don't touch the DB but we do touch the network - if (!info.quota.isPresent()) { + if (!info.quota().isPresent()) { // No quota? No update! return; } // If job has associated session, update quota in session - info.session.ifPresent(session -> { + info.session().ifPresent(session -> { try { - nmpi.setSessionStatusAndResources(session.id, "finished", - getResourceUsage(info.quota.get(), session.quotaUnits)); + nmpi.setSessionStatusAndResources(session.id(), "finished", + getResourceUsage(info.quota().get(), + session.quotaUnits())); } catch (BadRequestException e) { log.error(e.getResponse().readEntity(String.class)); throw e; @@ -558,10 +535,10 @@ final void finishJob(int jobId) { }); // If job has associated NMPI job, update quota on NMPI job - info.job.ifPresent(nmpiJob -> { + info.job().ifPresent(nmpiJob -> { try { - nmpi.setJobResources(nmpiJob.id, - getResourceUsage(info.quota.get(), nmpiJob.quotaUnits)); + nmpi.setJobResources(nmpiJob.id(), getResourceUsage( + info.quota().get(), nmpiJob.quotaUnits())); } catch (BadRequestException e) { log.error(e.getResponse().readEntity(String.class)); throw e; @@ -569,25 +546,15 @@ final void finishJob(int jobId) { }); } - private static final class Session { - private int id; - - private String quotaUnits; - + private record Session(int id, String quotaUnits) { private Session(Row r) { - this.id = r.getInt("session_id"); - this.quotaUnits = r.getString("quota_units"); + this(r.getInt("session_id"), r.getString("quota_units")); } } - private static final class NMPIJob { - private int id; - - private String quotaUnits; - + private record NMPIJob(int id, String quotaUnits) { private NMPIJob(Row r) { - this.id = r.getInt("nmpi_job_id"); - this.quotaUnits = r.getString("quota_units"); + this(r.getInt("nmpi_job_id"), r.getString("quota_units")); } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/Spalloc.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/Spalloc.java index b831a0e7f5..11a1d2ad44 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/Spalloc.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/Spalloc.java @@ -32,10 +32,10 @@ import static uk.ac.manchester.spinnaker.alloc.model.JobState.READY; import static uk.ac.manchester.spinnaker.alloc.model.PowerState.OFF; import static uk.ac.manchester.spinnaker.alloc.model.PowerState.ON; -import static uk.ac.manchester.spinnaker.alloc.security.SecurityConfig.MAY_SEE_JOB_DETAILS; import static uk.ac.manchester.spinnaker.utils.CollectionUtils.copy; import static uk.ac.manchester.spinnaker.utils.OptionalUtils.apply; +import java.io.Serial; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; @@ -50,7 +50,6 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PostFilter; import org.springframework.stereotype.Service; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -362,7 +361,6 @@ private static JobListEntryRecord makeJobListEntryRecord(Permit permit, } @Override - @PostFilter(MAY_SEE_JOB_DETAILS) public Optional getJob(Permit permit, int id) { return executeRead(conn -> getJob(id, conn).map(j -> (Job) j)); } @@ -374,7 +372,6 @@ private Optional getJob(int id, Connection conn) { } @Override - @PostFilter(MAY_SEE_JOB_DETAILS) public Optional getJobInfo(Permit permit, int id) { return executeRead(conn -> { try (var s = conn.query(GET_JOB); @@ -563,7 +560,7 @@ public Optional createJobForNMPIJob(String owner, int nmpiJobId, var quotaDetails = collab.get(); var job = execute(conn -> createJobInGroup( - owner, quotaDetails.collabId, descriptor, machineName, + owner, quotaDetails.collabId(), descriptor, machineName, tags, keepaliveInterval, originalRequest)); // On failure to get job, just return; shouldn't happen as quota checked // earlier, but just in case! @@ -572,7 +569,7 @@ public Optional createJobForNMPIJob(String owner, int nmpiJobId, } quotaManager.associateNMPIJob(job.get().getId(), nmpiJobId, - quotaDetails.quotaUnits); + quotaDetails.quotaUnits()); // Return the job created return job; @@ -626,17 +623,6 @@ private static Optional getUser(Connection conn, String userName) { } } - private class BoardLocated { - int boardId; - - int z; - - BoardLocated(Row row) { - boardId = row.getInt("board_id"); - z = row.getInt("z"); - } - } - /** * Resolve a machine name and {@link HasBoardCoords} to a board identifier. * @@ -655,20 +641,26 @@ private class BoardLocated { */ private Integer locateBoard(Connection conn, String machineName, HasBoardCoords b, boolean requireTriadRoot) { + record BoardLocated(int boardId, int z) { + BoardLocated(Row row) { + this(row.getInt("board_id"), row.getInt("z")); + } + } + try (var findTriad = conn.query(FIND_BOARD_BY_NAME_AND_XYZ); var findPhysical = conn.query(FIND_BOARD_BY_NAME_AND_CFB); var findIP = conn.query(FIND_BOARD_BY_NAME_AND_IP_ADDRESS)) { if (nonNull(b.triad)) { return findTriad.call1(BoardLocated::new, - machineName, b.triad.x, b.triad.y, b.triad.z) + machineName, b.triad.x(), b.triad.y(), b.triad.z()) .filter(board -> !requireTriadRoot || board.z == 0) .map(board -> board.boardId) .orElseThrow(() -> new IllegalArgumentException( NO_BOARD_MSG)); } else if (nonNull(b.physical)) { return findPhysical.call1( - BoardLocated::new, machineName, b.physical.c, - b.physical.f, b.physical.b) + BoardLocated::new, machineName, b.physical.c(), + b.physical.f(), b.physical.b()) .filter(board -> !requireTriadRoot || board.z == 0) .map(board -> board.boardId) .orElseThrow(() -> new IllegalArgumentException( @@ -723,8 +715,7 @@ private static String mergeDescription(HasChipLocation coreLocation, if (isNull(description)) { description = ""; } - if (coreLocation instanceof HasCoreLocation) { - var loc = (HasCoreLocation) coreLocation; + if (coreLocation instanceof HasCoreLocation loc) { description += format(" (at core %d of chip %s)", loc.getP(), loc.asChipLocation()); } else if (nonNull(coreLocation)) { @@ -734,14 +725,9 @@ private static String mergeDescription(HasChipLocation coreLocation, return description; } - private class Problem { - int boardId; - - Integer jobId; - + private record Problem(int boardId, Integer jobId) { Problem(Row row) { - boardId = row.getInt("board_id"); - jobId = row.getInt("job_id"); + this(row.getInt("board_id"), row.getInt("job_id")); } } @@ -784,28 +770,13 @@ private Optional reportProblem(Problem problem, }); } - private class Reported { - int boardId; - - int x; - - int y; - - int z; - - String address; - - int numReports; - + private record Reported(int boardId, int x, int y, int z, String address, + int numReports) { Reported(Row row) { - boardId = row.getInt("board_id"); - x = row.getInt("x"); - y = row.getInt("y"); - z = row.getInt("z"); - address = row.getString("address"); - numReports = row.getInt("numReports"); + this(row.getInt("board_id"), row.getInt("x"), row.getInt("y"), + row.getInt("z"), row.getString("address"), + row.getInt("numReports")); } - } /** @@ -847,7 +818,7 @@ private static DownLink makeDownLinkFromRow(Row row) { board2, row.getEnum("dir_2", Direction.class)); } - private class MachineImpl implements Machine { + private final class MachineImpl implements Machine { private final int id; private final boolean inService; @@ -903,14 +874,16 @@ public boolean waitForChange(Duration timeout) { } } + private BoardLocation boardLoc(Row row) { + return new BoardLocationImpl(row, this); + } + @Override public Optional getBoardByChip(HasChipLocation chip) { try (var conn = getConnection(); var findBoard = conn.query(findBoardByGlobalChip)) { - return conn.transaction(false, - () -> findBoard.call1( - row -> new BoardLocationImpl(row, this), id, - chip.getX(), chip.getY())); + return conn.transaction(false, () -> findBoard + .call1(this::boardLoc, id, chip.getX(), chip.getY())); } } @@ -920,9 +893,8 @@ public Optional getBoardByPhysicalCoords( try (var conn = getConnection(); var findBoard = conn.query(findBoardByPhysicalCoords)) { return conn.transaction(false, - () -> findBoard.call1( - row -> new BoardLocationImpl(row, this), id, - coords.c, coords.f, coords.b)); + () -> findBoard.call1(this::boardLoc, id, coords.c(), + coords.f(), coords.b())); } } @@ -932,9 +904,8 @@ public Optional getBoardByLogicalCoords( try (var conn = getConnection(); var findBoard = conn.query(findBoardByLogicalCoords)) { return conn.transaction(false, - () -> findBoard.call1( - row -> new BoardLocationImpl(row, this), id, - coords.x, coords.y, coords.z)); + () -> findBoard.call1(this::boardLoc, id, coords.x(), + coords.y(), coords.z())); } } @@ -943,9 +914,7 @@ public Optional getBoardByIPAddress(String address) { try (var conn = getConnection(); var findBoard = conn.query(findBoardByIPAddress)) { return conn.transaction(false, - () -> findBoard.call1( - row -> new BoardLocationImpl(row, this), id, - address)); + () -> findBoard.call1(this::boardLoc, id, address)); } } @@ -953,8 +922,10 @@ public Optional getBoardByIPAddress(String address) { public String getRootBoardBMPAddress() { try (var conn = getConnection(); var rootBMPaddr = conn.query(GET_ROOT_BMP_ADDRESS)) { - return conn.transaction(false, () -> rootBMPaddr.call1( - string("address"), id).orElse(null)); + return conn + .transaction(false, + () -> rootBMPaddr.call1(string("address"), id)) + .orElse(null); } } @@ -998,7 +969,7 @@ public List getDownLinks() { } } try (var conn = getConnection(); - var boardNumbers = conn.query(getDeadLinks)) { + var boardNumbers = conn.query(GET_DEAD_LINKS)) { var downLinks = conn.transaction(false, () -> boardNumbers .call(Spalloc::makeDownLinkFromRow, id)); synchronized (Spalloc.this) { @@ -1052,10 +1023,11 @@ public boolean isInService() { public String getBMPAddress(BMPCoords bmp) { try (var conn = getConnection(); var bmpAddr = conn.query(GET_BMP_ADDRESS)) { - return conn.transaction(false, - () -> bmpAddr - .call1(string("address"), id, bmp.getCabinet(), - bmp.getFrame()).orElse(null)); + return conn + .transaction(false, + () -> bmpAddr.call1(string("address"), id, + bmp.cabinet(), bmp.frame())) + .orElse(null); } } @@ -1064,17 +1036,15 @@ public List getBoardNumbers(BMPCoords bmp) { try (var conn = getConnection(); var boardNumbers = conn.query(GET_BMP_BOARD_NUMBERS)) { return conn.transaction(false, - () -> boardNumbers - .call(integer("board_num"), id, - bmp.getCabinet(), bmp.getFrame())); + () -> boardNumbers.call(integer("board_num"), id, + bmp.cabinet(), bmp.frame())); } } @Override public boolean equals(Object other) { // Equality is defined exactly by the database ID - return (other instanceof MachineImpl) - && (id == ((MachineImpl) other).id); + return (other instanceof MachineImpl m) && (id == m.id); } @Override @@ -1191,7 +1161,7 @@ private final class BoardReportSQL extends AbstractSQL { final Update insertReport = conn.update(INSERT_BOARD_REPORT); - final Query getReported = conn.query(getReportedBoards); + final Query getReported = conn.query(GET_REPORTED_BOARDS); final Update setFunctioning = conn.update(SET_FUNCTIONING_FIELD); @@ -1236,24 +1206,24 @@ void header(String issue, int numBoards, String who) { void chip(ReportedBoard board) { b.format("\tBoard for job (%d) chip %s\n", // - id, board.chip); + id, board.chip()); } void triad(ReportedBoard board) { b.format("\tBoard for job (%d) board (X:%d,Y:%d,Z:%d)\n", // - id, board.x, board.y, board.z); + id, board.x(), board.y(), board.z()); } void phys(ReportedBoard board) { b.format( "\tBoard for job (%d) board " + "[Cabinet:%d,Frame:%d,Board:%d]\n", // - id, board.cabinet, board.frame, board.board); + id, board.cabinet(), board.frame(), board.board()); } void ip(ReportedBoard board) { b.format("\tBoard for job (%d) board (IP: %s)\n", // - id, board.address); + id, board.address()); } void issue(int issueId) { @@ -1487,9 +1457,9 @@ public String reportIssue(IssueReportRequest report, Permit permit) { var result = q.transaction( () -> reportIssue(report, permit, email, q)); emailSender.sendServiceMail(email); - for (var m : report.boards.stream() + for (var m : report.boards().stream() .map(b -> q.getNamedMachine.call1( - r -> r.getInt("machine_id"), b.machine, true)) + r -> r.getInt("machine_id"), b.machine(), true)) .collect(toSet())) { if (m.isPresent()) { epochs.machineChanged(m.get()); @@ -1525,13 +1495,13 @@ public String reportIssue(IssueReportRequest report, Permit permit) { */ private String reportIssue(IssueReportRequest report, Permit permit, EmailBuilder email, BoardReportSQL q) throws ReportRollbackExn { - email.header(report.issue, report.boards.size(), permit.name); + email.header(report.issue(), report.boards().size(), permit.name); int userId = getUser(q.getConnection(), permit.name) .orElseThrow(() -> new ReportRollbackExn( "no such user: %s", permit.name)); - for (var board : report.boards) { + for (var board : report.boards()) { addIssueReport(q, getJobBoardForReport(q, board, email), - report.issue, userId, email); + report.issue(), userId, email); } return takeBoardsOutOfService(q, email).map(acted -> { email.footer(acted); @@ -1555,46 +1525,47 @@ private String reportIssue(IssueReportRequest report, Permit permit, private int getJobBoardForReport(BoardReportSQL q, ReportedBoard board, EmailBuilder email) throws ReportRollbackExn { Problem r; - if (nonNull(board.chip)) { + if (nonNull(board.chip())) { r = q.findBoardByChip - .call1(Problem::new, id, root, board.chip.getX(), - board.chip.getY()) - .orElseThrow(() -> new ReportRollbackExn(board.chip)); + .call1(Problem::new, id, root, board.chip().getX(), + board.chip().getY()) + .orElseThrow(() -> new ReportRollbackExn(board.chip())); email.chip(board); - } else if (nonNull(board.x)) { + } else if (nonNull(board.x())) { r = q.findBoardByTriad - .call1(Problem::new, machineId, board.x, board.y, - board.z) + .call1(Problem::new, machineId, board.x(), board.y(), + board.z()) .orElseThrow(() -> new ReportRollbackExn( - "triad (%s,%s,%s) not in machine", board.x, - board.y, board.z)); + "triad (%s,%s,%s) not in machine", board.x(), + board.y(), board.z())); if (isNull(r.jobId) || id != r.jobId) { throw new ReportRollbackExn( - "triad (%s,%s,%s) not allocated to job %d", board.x, - board.y, board.z, id); + "triad (%s,%s,%s) not allocated to job %d", + board.x(), board.y(), board.z(), id); } email.triad(board); - } else if (nonNull(board.cabinet)) { + } else if (nonNull(board.cabinet())) { r = q.findBoardPhys - .call1(Problem::new, machineId, board.cabinet, - board.frame, board.board) + .call1(Problem::new, machineId, board.cabinet(), + board.frame(), board.board()) .orElseThrow(() -> new ReportRollbackExn( "physical board [%s,%s,%s] not in machine", - board.cabinet, board.frame, board.board)); + board.cabinet(), board.frame(), board.board())); if (isNull(r.jobId) || id != r.jobId) { throw new ReportRollbackExn( "physical board [%s,%s,%s] not allocated to job %d", - board.cabinet, board.frame, board.board, id); + board.cabinet(), board.frame(), board.board(), id); } email.phys(board); - } else if (nonNull(board.address)) { - r = q.findBoardNet.call1(Problem::new, machineId, board.address) + } else if (nonNull(board.address())) { + r = q.findBoardNet + .call1(Problem::new, machineId, board.address()) .orElseThrow(() -> new ReportRollbackExn( - "board at %s not in machine", board.address)); + "board at %s not in machine", board.address())); if (isNull(r.jobId) || id != r.jobId) { throw new ReportRollbackExn( "board at %s not allocated to job %d", - board.address, id); + board.address(), id); } email.ip(board); } else { @@ -1666,7 +1637,7 @@ public void forgetProxy(ProxyCore proxy) { @Override public boolean equals(Object other) { // Equality is defined exactly by the database ID - return (other instanceof JobImpl) && (id == ((JobImpl) other).id); + return (other instanceof JobImpl j) && (id == j.id); } @Override @@ -1906,6 +1877,7 @@ public Job getJob() { } static class PartialJobException extends IllegalStateException { + @Serial private static final long serialVersionUID = 2997856394666135483L; PartialJobException() { @@ -1929,6 +1901,7 @@ class ReportRollbackExn extends RuntimeException { } abstract class GroupsException extends RuntimeException { + @Serial private static final long serialVersionUID = 6607077117924279611L; GroupsException(String message) { @@ -1941,6 +1914,7 @@ abstract class GroupsException extends RuntimeException { } class NoSuchGroupException extends GroupsException { + @Serial private static final long serialVersionUID = 5193818294198205503L; @FormatMethod @@ -1950,6 +1924,7 @@ class NoSuchGroupException extends GroupsException { } class MultipleGroupsException extends GroupsException { + @Serial private static final long serialVersionUID = 6284332340565334236L; @FormatMethod diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/SpallocAPI.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/SpallocAPI.java index 1dcedfed35..ced3655fd3 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/SpallocAPI.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/allocator/SpallocAPI.java @@ -31,17 +31,16 @@ import java.util.Optional; import java.util.Set; -import javax.validation.Valid; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - import org.springframework.security.access.prepost.PostFilter; import org.springframework.security.access.prepost.PreAuthorize; import com.google.errorprone.annotations.Keep; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.alloc.compat.V1CompatService; import uk.ac.manchester.spinnaker.alloc.model.BoardCoords; import uk.ac.manchester.spinnaker.alloc.model.ConnectionInfo; @@ -328,7 +327,8 @@ void reportProblem(@IPAddress String address, HasChipLocation coreLocation, * @see CreateDimensionsAt * @see CreateBoard */ - abstract class CreateDescriptor { + abstract sealed class CreateDescriptor + permits CreateDimensions, CreateNumBoards, HasBoardCoords { /** * The maximum number of dead boards tolerated in the allocation. * Ignored when asking for a single board. @@ -449,7 +449,8 @@ public int getArea() { } /** Some requests have the locations of boards. */ - abstract class HasBoardCoords extends CreateDescriptor { + abstract sealed class HasBoardCoords + extends CreateDescriptor permits CreateDimensionsAt, CreateBoard { /** The logical coordinates, or {@code null}. */ @Valid public final TriadCoords triad; @@ -763,7 +764,9 @@ interface CreateVisitor { * The descriptor. * @return The result of the visiting. */ - T numBoards(@NotNull CreateNumBoards createNumBoards); + default T numBoards(@NotNull CreateNumBoards createNumBoards) { + return null; + } /** * Visit a descriptor. @@ -772,7 +775,9 @@ interface CreateVisitor { * The descriptor. * @return The result of the visiting. */ - T dimensionsAt(@NotNull CreateDimensionsAt createDimensionsAt); + default T dimensionsAt(@NotNull CreateDimensionsAt createDimensionsAt) { + return null; + } /** * Visit a descriptor. @@ -781,7 +786,9 @@ interface CreateVisitor { * The descriptor. * @return The result of the visiting. */ - T dimensions(@NotNull CreateDimensions createDimensions); + default T dimensions(@NotNull CreateDimensions createDimensions) { + return null; + } /** * Visit a descriptor. @@ -790,7 +797,9 @@ interface CreateVisitor { * The descriptor. * @return The result of the visiting. */ - T board(@NotNull CreateBoard createBoard); + default T board(@NotNull CreateBoard createBoard) { + return null; + } } /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/BMPController.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/BMPController.java index f3fe15f957..3019eec87c 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/BMPController.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/BMPController.java @@ -18,6 +18,7 @@ import static java.lang.String.format; import static java.lang.Thread.currentThread; import static java.lang.Thread.sleep; +import static java.time.Instant.now; import static java.util.Objects.requireNonNull; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.alloc.bmp.NonBootOperation.GET_SERIAL; @@ -30,7 +31,6 @@ import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; -import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -41,8 +41,6 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -54,6 +52,7 @@ import com.google.errorprone.annotations.RestrictedApi; import com.google.errorprone.annotations.concurrent.GuardedBy; +import jakarta.annotation.PostConstruct; import uk.ac.manchester.spinnaker.alloc.ForTestingOnly; import uk.ac.manchester.spinnaker.alloc.ServiceMasterControl; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.AllocatorProperties; @@ -217,7 +216,7 @@ public void triggerSearch(Collection bmps) { for (var b : bmps) { var worker = workers.get(b); if (worker != null) { - scheduler.schedule(() -> worker.run(), Instant.now()); + scheduler.schedule(worker::run, now()); } else { log.error("Could not find worker for BMP {}", b); } @@ -229,7 +228,8 @@ private interface ThrowingAction { void act() throws ProcessException, IOException, InterruptedException; } - private abstract class Request { + private abstract sealed class Request + permits BoardRequest, PowerRequest { final int bmpId; private int numTries = 0; @@ -455,15 +455,15 @@ private final class PowerRequest extends Request { List powerChanges) { super(bmpId); for (var change : powerChanges) { - if (change.power) { - powerOnBoards.add(new BMPBoard(change.boardNum)); + if (change.power()) { + powerOnBoards.add(new BMPBoard(change.boardNum())); } else { - powerOffBoards.add(new BMPBoard(change.boardNum)); + powerOffBoards.add(new BMPBoard(change.boardNum())); } - change.offLinks.stream().forEach(link -> - linkRequests.add(new Link(change.boardNum, link))); - changeIds.add(change.changeId); - boardToId.put(change.boardNum, change.boardId); + change.offLinks().stream().forEach(link -> + linkRequests.add(new Link(change.boardNum(), link))); + changeIds.add(change.changeId()); + boardToId.put(change.boardNum(), change.boardId); } this.jobId = jobId; this.from = from; @@ -674,7 +674,7 @@ private Optional getBoardId(HasBMPLocation addr) { } private Integer getBoardId(BMPBoard board) { - return boardToId.get(board.board); + return boardToId.get(board.board()); } } @@ -845,20 +845,11 @@ boolean tryProcessRequest(SpiNNakerControl controller) throws InterruptedException { return bmpAction(() -> { switch (op) { - case WRITE_BL: - writeBlacklist(controller); - break; - case READ_BL: - readBlacklist(controller); - break; - case GET_SERIAL: - readSerial(controller); - break; - case READ_TEMP: - readTemps(controller); - break; - default: - throw new IllegalArgumentException(); + case WRITE_BL -> writeBlacklist(controller); + case READ_BL -> readBlacklist(controller); + case GET_SERIAL -> readSerial(controller); + case READ_TEMP -> readTemps(controller); + default -> throw new IllegalArgumentException(); } epochs.blacklistChanged(boardId); epochs.machineChanged(machineId); @@ -987,34 +978,20 @@ public String toString() { } } - private class PowerChange { - final Integer changeId; - - final int jobId; - - final Integer boardId; - - final Integer boardNum; - - final boolean power; - - final JobState from; - - final JobState to; - - final List offLinks; - + private record PowerChange(Integer changeId, int jobId, Integer boardId, + Integer boardNum, boolean power, JobState from, JobState to, + List offLinks) { PowerChange(Row row) { - changeId = row.getInteger("change_id"); - jobId = row.getInt("job_id"); - boardId = row.getInteger("board_id"); - boardNum = row.getInteger("board_num"); - power = row.getBoolean("power"); - from = row.getEnum("from_state", JobState.class); - to = row.getEnum("to_state", JobState.class); - offLinks = List.of(Direction.values()).stream().filter( - link -> !row.getBoolean(link.columnName)).collect( - Collectors.toList()); + this(row.getInteger("change_id"), // + row.getInt("job_id"), // + row.getInteger("board_id"), // + row.getInteger("board_num"), // + row.getBoolean("power"), + row.getEnum("from_state", JobState.class), + row.getEnum("to_state", JobState.class), + List.of(Direction.values()).stream() + .filter(link -> !row.getBoolean(link.columnName)) + .collect(Collectors.toList())); } boolean isSameJob(PowerChange p) { @@ -1086,8 +1063,8 @@ private List getRequestedOperations() { } if (!jobChanges.isEmpty()) { log.debug("Running job changes {}", jobChanges); - requests.add(new PowerRequest(bmpId, change.jobId, - change.from, change.to, jobChanges)); + requests.add(new PowerRequest(bmpId, change.jobId(), + change.from(), change.to(), jobChanges)); } } @@ -1164,15 +1141,13 @@ void processRequests(long millis) throws IOException, SpinnmanException, InterruptedException; /** - * Get the last BMP exception. + * Get the most recently thrown BMP processing exception. * - * @return The exception. + * @return Current processing exception. */ Throwable getBmpException(); - /** - * Clear the last BMP exception. - */ + /** Clear the current processing exception. */ void clearBmpException(); } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistStore.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistStore.java index 362f74120b..c652a9cd2d 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistStore.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistStore.java @@ -34,6 +34,7 @@ import uk.ac.manchester.spinnaker.alloc.db.DatabaseAwareBean; import uk.ac.manchester.spinnaker.alloc.db.Row; import uk.ac.manchester.spinnaker.machine.ChipLocation; +import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.machine.Direction; import uk.ac.manchester.spinnaker.messages.model.Blacklist; @@ -61,17 +62,6 @@ public Optional readBlacklist(int boardId) { return executeRead(conn -> readBlacklist(conn, boardId)); } - private class DeadLink { - ChipLocation location; - - Direction direction; - - DeadLink(Row row) { - location = new ChipLocation(row.getInt("x"), row.getInt("y")); - direction = row.getEnum("direction", Direction.class); - } - } - /** * Read a blacklist from the database. * @@ -84,6 +74,13 @@ private class DeadLink { * If database access fails. */ private Optional readBlacklist(Connection conn, int boardId) { + record DeadLink(ChipLocation location, Direction direction) { + DeadLink(Row row) { + this(new ChipLocation(row.getInt("x"), row.getInt("y")), + row.getEnum("direction", Direction.class)); + } + } + try (var blChips = conn.query(GET_BLACKLISTED_CHIPS); var blCores = conn.query(GET_BLACKLISTED_CORES); var blLinks = conn.query(GET_BLACKLISTED_LINKS)) { @@ -91,14 +88,12 @@ private Optional readBlacklist(Connection conn, int boardId) { stream(blChips.call(chip("x", "y"), boardId)).toSet(); var blacklistedCores = stream(blCores.call(core("x", "y", "p"), boardId)) - .toCollectingMap( - HashSet::new, c -> c.asChipLocation(), - c -> c.getP()); - var blacklistedLinks = - stream(blLinks.call(DeadLink::new, boardId)) - .toCollectingMap( - () -> noneOf(Direction.class), d -> d.location, - d -> d.direction); + .toCollectingMap(HashSet::new, + CoreLocation::asChipLocation, + CoreLocation::getP); + var blacklistedLinks = stream(blLinks.call(DeadLink::new, boardId)) + .toCollectingMap(() -> noneOf(Direction.class), + DeadLink::location, DeadLink::direction); if (blacklistedChips.isEmpty() && blacklistedCores.isEmpty() && blacklistedLinks.isEmpty()) { diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/FirmwareLoader.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/FirmwareLoader.java index 0cb109b7b9..2931318a18 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/FirmwareLoader.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/FirmwareLoader.java @@ -21,6 +21,7 @@ import static java.lang.System.currentTimeMillis; import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.time.Instant.ofEpochSecond; import static java.util.Arrays.stream; import static java.util.stream.Collectors.toList; import static org.apache.commons.io.IOUtils.buffer; @@ -35,15 +36,16 @@ import static uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters.LEDO; import static uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters.SCRM; import static uk.ac.manchester.spinnaker.messages.model.FPGAMainRegisters.SLEN; -import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.slice; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import static uk.ac.manchester.spinnaker.utils.UnitConstants.MSEC_PER_SEC; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.Serial; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -51,14 +53,13 @@ import java.util.Properties; import java.util.zip.CRC32; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; +import jakarta.annotation.PostConstruct; import uk.ac.manchester.spinnaker.alloc.model.Prototype; import uk.ac.manchester.spinnaker.machine.MemoryLocation; import uk.ac.manchester.spinnaker.machine.board.BMPBoard; @@ -168,6 +169,7 @@ public FirmwareLoader(BMPTransceiverInterface txrx, BMPBoard board) { /** Base class of exceptions thrown by the firmware loader. */ public abstract static class FirmwareLoaderException extends RuntimeException { + @Serial private static final long serialVersionUID = -7057612243855126410L; FirmwareLoaderException(String msg) { @@ -177,6 +179,7 @@ public abstract static class FirmwareLoaderException /** An update of the firmware on a BMP failed. */ public static class UpdateFailedException extends FirmwareLoaderException { + @Serial private static final long serialVersionUID = 7925582707336953554L; /** The data read back from the BMP. */ @@ -184,13 +187,13 @@ public static class UpdateFailedException extends FirmwareLoaderException { UpdateFailedException(ByteBuffer data) { super("failed to update flash data correctly!"); - this.data = data.asReadOnlyBuffer(); - this.data.order(LITTLE_ENDIAN); + this.data = readOnly(data); } } /** A CRC check failed. */ public static class CRCFailedException extends FirmwareLoaderException { + @Serial private static final long serialVersionUID = -4111893327837084643L; /** The CRC calculated by the BMP. */ @@ -204,6 +207,7 @@ public static class CRCFailedException extends FirmwareLoaderException { /** A data chunk was too large for the firmware loader to handle. */ public static class TooLargeException extends FirmwareLoaderException { + @Serial private static final long serialVersionUID = -9025065456329109710L; TooLargeException(long size) { @@ -221,8 +225,7 @@ private static class FlashDataSector { final ByteBuffer buf; FlashDataSector() { - buf = ByteBuffer.allocate(DATA_SECTOR_LENGTH); - buf.order(LITTLE_ENDIAN); + buf = alloc(DATA_SECTOR_LENGTH); } static FlashDataSector registers(int numItems, List data) { @@ -274,7 +277,7 @@ private void bitfileHeader(int mtime, int crc, FPGA chip, int timestamp, buf.putShort((short) (BITFILE_ENABLED_FLAG + chip.bits)); buf.putInt(timestamp); buf.putInt(crc); - buf.putInt(baseAddress.address); + buf.putInt(baseAddress.address()); buf.putInt(length); buf.putInt(mtime); @@ -298,14 +301,14 @@ private void bitfileName(String name) { * just immediately, but also during BMP boot. * * @author Donal Fellows + * @param fpga + * Which FPGA's register to set + * @param address + * The location of the register to set in the FPGA address space + * @param value + * What value to set */ - public static class RegisterSet { - private final FPGA fpga; - - private final MemoryLocation address; - - private final int value; - + public record RegisterSet(FPGA fpga, MemoryLocation address, int value) { /** * @param fpga * Which FPGA's registers to set @@ -315,9 +318,7 @@ public static class RegisterSet { * The value to set */ public RegisterSet(FPGA fpga, FPGAMainRegisters register, int value) { - this.fpga = fpga; - this.address = register.getAddress(); - this.value = value; + this(fpga, register.getAddress(), value); } /** @@ -332,9 +333,7 @@ public RegisterSet(FPGA fpga, FPGAMainRegisters register, int value) { */ public RegisterSet(FPGA fpga, FPGALinkRegisters register, int bank, int value) { - this.fpga = fpga; - this.address = register.address(bank); - this.value = value; + this(fpga, register.address(bank), value); } } @@ -359,7 +358,7 @@ private ByteBuffer readFlashDataHead() private static int crc(ByteBuffer buffer, int from, int len) { var crc = new CRC32(); - crc.update(slice(buffer, from, len)); + crc.update(buffer.slice(from, len)); return (int) (crc.getValue() & CRC_MASK); } @@ -401,8 +400,7 @@ private void logBMPVersion() log.info("BMP INFO: {}", format("%s %s at %s:%s (built %s) [C=%s, F=%s, B=%s]", info.name, info.versionNumber, info.hardware, - info.physicalCPUID, - Instant.ofEpochSecond(info.buildDate), + info.physicalCPUID, ofEpochSecond(info.buildDate), info.location.getCabinet(), info.location.getFrame(), info.location.getBoard())); } @@ -417,19 +415,13 @@ private void listFPGABootChunks() throws ProcessException, IOException, InterruptedException { var data = readFlashDataHead(); for (int i = 0; i < NUM_DATA_SECTORS; i++) { - var chunk = slice(data, DATA_SECTOR_CHUNK_SIZE * i, - DATA_SECTOR_CHUNK_SIZE); + var chunk = data.slice(DATA_SECTOR_CHUNK_SIZE * i, + DATA_SECTOR_CHUNK_SIZE).order(LITTLE_ENDIAN); byte type = chunk.get(); switch (DataSectorTypes.get(type)) { - case REGISTER: - logRegisterSets(chunk); - break; - case BITFILE: - logFPGABootBitfile(chunk, i); - break; - default: - // Ignore the chunk - break; + case REGISTER -> logRegisterSets(chunk); + case BITFILE -> logFPGABootBitfile(chunk, i); + default -> log.trace("ignoring chunk with type code {}", type); } } } @@ -483,10 +475,8 @@ private void logFPGABootBitfile(ByteBuffer data, int i) { length, crc)); log.info("FPGA BOOT: File {}", new String(filenameBytes, 0, size, UTF_8).strip()); - log.info("FPGA BOOT: Written {}", - Instant.ofEpochSecond(time)); - log.info("FPGA BOOT: ModTime {}", - Instant.ofEpochSecond(mtime)); + log.info("FPGA BOOT: Written {}", ofEpochSecond(time)); + log.info("FPGA BOOT: ModTime {}", ofEpochSecond(mtime)); } /** @@ -508,8 +498,8 @@ public void setupRegisters(RegisterSet... settings) throws ProcessException, IOException, InterruptedException { var data = new ArrayList(); for (var r : settings) { - data.add(r.address.address | r.fpga.value); - data.add(r.value); + data.add(r.address().address() | r.fpga().value); + data.add(r.value()); } var sector = FlashDataSector.registers(settings.length, data); diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/Link.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/Link.java index 5591fb5b9a..cc24088bb1 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/Link.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/Link.java @@ -23,14 +23,12 @@ * inter-board link to be off. * * @author Donal Fellows + * @param board + * The database ID of the board that the FPGA is located on. + * @param link + * Which link (and hence which FPGA). */ -public final class Link { - /** The database ID of the board that the FPGA is located on. */ - private final BMPBoard board; - - /** Which link (and hence which FPGA). */ - private final Direction link; - +public record Link(BMPBoard board, Direction link) { /** * Create a request. * @@ -40,22 +38,11 @@ public final class Link { * Which link (and hence which FPGA). */ Link(int board, Direction link) { - this.board = new BMPBoard(board); - this.link = link; + this(new BMPBoard(board), link); } @Override public String toString() { return "Link(" + board + "," + link + ":OFF)"; } - - /** @return The database ID of the board that the FPGA is located on. */ - public BMPBoard getBoard() { - return board; - } - - /** @return Which link (and hence which FPGA). */ - public Direction getLink() { - return link; - } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/PhysicalSerialMapping.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/PhysicalSerialMapping.java index 8c0615368c..7adf9b4600 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/PhysicalSerialMapping.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/PhysicalSerialMapping.java @@ -17,8 +17,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.function.Predicate.not; -import static org.slf4j.LoggerFactory.getLogger; import static org.apache.commons.io.IOUtils.buffer; +import static org.slf4j.LoggerFactory.getLogger; import java.io.IOException; import java.io.InputStreamReader; @@ -26,13 +26,13 @@ import java.util.HashMap; import java.util.Map; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; +import jakarta.annotation.PostConstruct; + /** * Holds the mapping between physical board IDs and BMP IDs. Physical board IDs * were allocated by the manufacturer of the boards (Norcott) and are @@ -42,6 +42,9 @@ *

* The original form of blacklists stores them according to their physical board * ID because that's what is easily available during commissioning. + *

+ * This class does not need to use locking to guard its internal state; after + * the bean enters service, that state is never modified. * * @author Donal Fellows */ diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/SpiNNaker1.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/SpiNNaker1.java index 1ea2982f97..632cb0a0ad 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/SpiNNaker1.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/SpiNNaker1.java @@ -24,13 +24,12 @@ import java.util.List; import java.util.Map; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import jakarta.annotation.PostConstruct; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.TxrxProperties; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.Machine; import uk.ac.manchester.spinnaker.alloc.bmp.FirmwareLoader.FirmwareLoaderException; @@ -148,7 +147,7 @@ void initTransceiver() } /** Notes that a board probably needs its FPGA definitions reloading. */ - private static class FPGAReloadRequired extends Exception { + private static final class FPGAReloadRequired extends Exception { private static final long serialVersionUID = 1L; final BMPBoard board; @@ -210,7 +209,7 @@ private boolean isGoodFPGA(BMPBoard board, FPGA fpga) private boolean canBoardManageFPGAs(BMPBoard board) throws ProcessException, IOException, InterruptedException { var vi = txrx.readBMPVersion(board); - return vi.versionNumber.majorVersion >= BMP_VERSION_MIN; + return vi.versionNumber.majorVersion() >= BMP_VERSION_MIN; } /** @@ -222,8 +221,8 @@ private boolean canBoardManageFPGAs(BMPBoard board) @Override public void setLinkOff(Link link) throws ProcessException, IOException, InterruptedException { - var board = link.getBoard(); - var d = link.getLink(); + var board = link.board(); + var d = link.link(); // skip FPGA link configuration if old BMP version if (!canBoardManageFPGAs(board)) { return; @@ -258,7 +257,7 @@ private boolean hasGoodFPGAs(BMPBoard board) public void powerOnAndCheck(List boards) throws ProcessException, InterruptedException, IOException { var boardsToPower = boards; - log.debug("Power on and check boards {} for BMP {}", boards, bmp); + log.info("Power on and check boards {} for BMP {}", boards, bmp); boolean reloadDone = false; // so we only do firmware loading once for (int attempt = 1; attempt <= props.getFpgaAttempts(); attempt++) { if (attempt > 1) { @@ -317,24 +316,28 @@ public void powerOnAndCheck(List boards) @Override public void powerOff(List boards) throws ProcessException, InterruptedException, IOException { + log.info("Power off boards {} for BMP {}", boards, bmp); txrx.powerOff(boards); } @Override public String readSerial(BMPBoard board) throws ProcessException, IOException, InterruptedException { + log.info("Read serial number from board {} for BMP {}", board, bmp); return txrx.readBoardSerialNumber(board); } @Override public Blacklist readBlacklist(BMPBoard board) throws ProcessException, IOException, InterruptedException { + log.info("Read blacklist from board {} for BMP {}", board, bmp); return txrx.readBlacklist(board); } @Override public void writeBlacklist(BMPBoard board, Blacklist blacklist) throws ProcessException, InterruptedException, IOException { + log.info("Write blacklist to board {} for BMP {}", board, bmp); txrx.writeBlacklist(board, blacklist); } @@ -346,6 +349,7 @@ public ADCInfo readTemp(BMPBoard board) @Override public void ping(List boards) { + log.info("Ping boards {} for BMP {}", boards, bmp); boards.parallelStream().forEach(id -> { var address = boardAddresses.get(id); if (Ping.ping(address) != 0) { diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/TransceiverFactory.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/TransceiverFactory.java index 820db18ef2..46b52cd351 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/TransceiverFactory.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/TransceiverFactory.java @@ -15,21 +15,18 @@ */ package uk.ac.manchester.spinnaker.alloc.bmp; -import static java.util.Objects.hash; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.messages.Constants.SCP_SCAMP_PORT; import static uk.ac.manchester.spinnaker.utils.InetFactory.getByName; import static uk.ac.manchester.spinnaker.utils.Ping.ping; import java.io.IOException; +import java.io.Serial; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -37,6 +34,8 @@ import com.google.errorprone.annotations.RestrictedApi; import com.google.errorprone.annotations.concurrent.GuardedBy; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import uk.ac.manchester.spinnaker.alloc.ForTestingOnly; import uk.ac.manchester.spinnaker.alloc.ServiceMasterControl; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.TxrxProperties; @@ -68,29 +67,7 @@ public class TransceiverFactory implements TransceiverFactoryAPI { private static final Logger log = getLogger(TransceiverFactory.class); - private static final class Key { - final String machine; - - final BMPCoords bmp; - - Key(String machine, BMPCoords bmp) { - this.machine = machine; - this.bmp = bmp; - } - - @Override - public boolean equals(Object o) { - if (o instanceof Key) { - var other = (Key) o; - return machine.equals(other.machine) && bmp.equals(other.bmp); - } - return false; - } - - @Override - public int hashCode() { - return hash(machine, bmp); - } + private record Key(String machine, BMPCoords bmp) { } @GuardedBy("itself") @@ -130,18 +107,19 @@ public BMPTransceiverInterface getTransciever(Machine machineDescription, } } catch (TransceiverFactoryException e) { var t = e.getCause(); - if (t instanceof IOException) { - throw (IOException) t; - } else if (t instanceof SpinnmanException) { - throw (SpinnmanException) t; - } else if (t instanceof InterruptedException) { - throw (InterruptedException) t; + if (t instanceof IOException ioe) { + throw ioe; + } else if (t instanceof SpinnmanException se) { + throw se; + } else if (t instanceof InterruptedException ie) { + throw ie; } throw e; } } private static class TransceiverFactoryException extends RuntimeException { + @Serial private static final long serialVersionUID = 2102592240724419836L; TransceiverFactoryException(String msg, Exception e) { @@ -213,7 +191,7 @@ private Transceiver makeTransceiver(BMPConnectionData data) throw e; } log.error("failed to connect to BMP; will ping and retry", e); - log.debug("ping result was {}", ping(data.ipAddress)); + log.debug("ping result was {}", ping(data.ipAddress())); } } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/Command.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/Command.java index 99f82a99db..5c28100cf6 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/Command.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/Command.java @@ -22,12 +22,12 @@ import java.util.List; import java.util.Map; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + /** * The encoded form of a command to the server. This is basically a Python * call encoded (except that no argument is a live object). diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/ExceptionResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/ExceptionResponse.java index c4da80d07b..52d6a85ccc 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/ExceptionResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/ExceptionResponse.java @@ -15,31 +15,12 @@ */ package uk.ac.manchester.spinnaker.alloc.compat; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NON_PRIVATE; -import static java.util.Objects.isNull; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * A message indicating an operation failed. * * @author Donal Fellows + * @param exception + * The exception message. */ -@JsonAutoDetect(setterVisibility = NON_PRIVATE) -final class ExceptionResponse { - private String exception; - - ExceptionResponse(String message) { - exception = message; - } - - @JsonProperty("exception") - public String getException() { - return exception; - } - - void setException(String exception) { - this.exception = isNull(exception) ? "" : exception.toString(); - } +record ExceptionResponse(String exception) { } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/JobNotifyMessage.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/JobNotifyMessage.java index 73fca63f50..fe05ed2a3f 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/JobNotifyMessage.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/JobNotifyMessage.java @@ -15,35 +15,17 @@ */ package uk.ac.manchester.spinnaker.alloc.compat; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NON_PRIVATE; - import java.util.List; -import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonProperty; /** * A job notification message. * * @author Donal Fellows + * @param jobsChanged + * What jobs have had their state change */ -@JsonAutoDetect(setterVisibility = NON_PRIVATE) -final class JobNotifyMessage { - private List jobsChanged; - - JobNotifyMessage(List changes) { - jobsChanged = changes; - } - - /** - * @return the jobs changed - */ - @JsonProperty("jobs_changed") - public List getJobsChanged() { - return jobsChanged; - } - - void setJobsChanged(List jobsChanged) { - this.jobsChanged = jobsChanged; - } +record JobNotifyMessage( + @JsonProperty("jobs_changed") List jobsChanged) { } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/MachineNotifyMessage.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/MachineNotifyMessage.java index c17b959635..5f063ca5a0 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/MachineNotifyMessage.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/MachineNotifyMessage.java @@ -15,35 +15,17 @@ */ package uk.ac.manchester.spinnaker.alloc.compat; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NON_PRIVATE; - import java.util.List; -import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonProperty; /** * A machine notification message. * * @author Donal Fellows + * @param machinesChanged + * What machines have had allocations on them change. */ -@JsonAutoDetect(setterVisibility = NON_PRIVATE) -final class MachineNotifyMessage { - private List machinesChanged; - - MachineNotifyMessage(List changes) { - machinesChanged = changes; - } - - /** - * @return the machines changed - */ - @JsonProperty("machines_changed") - public List getMachinesChanged() { - return machinesChanged; - } - - void setMachinesChanged(List machinesChanged) { - this.machinesChanged = machinesChanged; - } +record MachineNotifyMessage( + @JsonProperty("machines_changed") List machinesChanged) { } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/ReturnResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/ReturnResponse.java index 3149929098..2ad1c20731 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/ReturnResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/ReturnResponse.java @@ -15,30 +15,14 @@ */ package uk.ac.manchester.spinnaker.alloc.compat; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NON_PRIVATE; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonProperty; /** * A message indicating an operation succeeded. * * @author Donal Fellows + * @param returnValue + * The returned value */ -@JsonAutoDetect(setterVisibility = NON_PRIVATE) -final class ReturnResponse { - private Object returnValue; - - ReturnResponse(Object value) { - returnValue = value; - } - - @JsonProperty("return") - public Object getReturnValue() { - return returnValue; - } - - void setReturnValue(Object returnValue) { - this.returnValue = returnValue; - } +record ReturnResponse(@JsonProperty("return") Object returnValue) { } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/SaneParameter.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/SaneParameter.java index c8c773c748..f1da204af5 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/SaneParameter.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/SaneParameter.java @@ -25,10 +25,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; /** * Validates that an argument is a sane value to pass in a classic spalloc API @@ -69,8 +69,8 @@ class IsSaneValidator implements ConstraintValidator { @Override public boolean isValid(Object value, ConstraintValidatorContext context) { - if (value instanceof String) { - return !((String) value).isBlank(); + if (value instanceof String s) { + return !s.isBlank(); } else { return (value instanceof Boolean) || (value instanceof Number); } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/Utils.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/Utils.java index c84d51c056..0c6129e8f8 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/Utils.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/Utils.java @@ -112,10 +112,10 @@ static Integer parseDec(Object value) { int n; if (isNull(value)) { return null; - } else if (value instanceof Number) { - n = ((Number) value).intValue(); - } else if (value instanceof String) { - n = parseInt((String) value); + } else if (value instanceof Number num) { + n = num.intValue(); + } else if (value instanceof String s) { + n = parseInt(s); } else { throw new IllegalArgumentException( "needed a number, got a " + value.getClass().getName()); @@ -222,18 +222,14 @@ static Double timestamp(Instant instant) { * @return The converted state. */ static State state(JobState state) { - switch (state) { - case QUEUED: - return State.QUEUED; - case POWER: - return State.POWER; - case READY: - return State.READY; - case DESTROYED: - return State.DESTROYED; - default: - return State.UNKNOWN; - } + // So trivial... + return switch (state) { + case QUEUED -> State.QUEUED; + case POWER -> State.POWER; + case READY -> State.READY; + case DESTROYED -> State.DESTROYED; + default -> State.UNKNOWN; + }; } /** @@ -244,8 +240,7 @@ static State state(JobState state) { * @return The converted coordinate. */ static BoardCoordinates board(BoardCoords coords) { - return new BoardCoordinates(coords.getX(), coords.getY(), - coords.getZ()); + return new BoardCoordinates(coords.x(), coords.y(), coords.z()); } /** @@ -256,13 +251,13 @@ static BoardCoordinates board(BoardCoords coords) { * @return A stream of ends of the link. */ static Stream boardLinks(DownLink downLink) { - var bl1 = new BoardLink(downLink.end1.board.getX(), - downLink.end1.board.getY(), downLink.end1.board.getZ(), - downLink.end1.direction.ordinal()); + var bl1 = new BoardLink(downLink.end1().board().x(), + downLink.end1().board().y(), downLink.end1().board().z(), + downLink.end1().direction().ordinal()); - var bl2 = new BoardLink(downLink.end2.board.getX(), - downLink.end2.board.getY(), downLink.end2.board.getZ(), - downLink.end2.direction.ordinal()); + var bl2 = new BoardLink(downLink.end2().board().x(), + downLink.end2().board().y(), downLink.end2().board().z(), + downLink.end2().direction().ordinal()); return List.of(bl1, bl2).stream(); } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatService.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatService.java index 467ab7f17e..1c9e6d5b7c 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatService.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatService.java @@ -36,9 +36,6 @@ import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - import org.slf4j.Logger; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -46,8 +43,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.google.errorprone.annotations.RestrictedApi; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import uk.ac.manchester.spinnaker.alloc.ForTestingOnly; import uk.ac.manchester.spinnaker.alloc.SpallocProperties; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.CompatibilityProperties; @@ -100,7 +101,8 @@ public class V1CompatService { V1CompatService() { mapper = JsonMapper.builder().propertyNamingStrategy(SNAKE_CASE) - .build(); + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()).build(); var group = new ThreadGroup("spalloc-legacy-service"); var counter = new ValueHolder<>(1); threadFactory = r -> { @@ -227,7 +229,7 @@ private void acceptConnections() { private boolean acceptConnection() { try { var service = getTask(serv.accept()); - executor.execute(() -> service.handleConnection()); + executor.execute(service::handleConnection); } catch (SocketException e) { // Check here; interrupt = shutting down = no errors, please if (interrupted()) { @@ -287,7 +289,7 @@ public Future launchInstance(PipedWriter in, PipedReader out) throws Exception { var service = taskFactory.getObject(V1CompatService.this, new PipedReader(in), new PipedWriter(out)); - return executor.submit(() -> service.handleConnection()); + return executor.submit(service::handleConnection); } }; } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatTask.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatTask.java index fd9d81f993..e67cf57a06 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatTask.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatTask.java @@ -43,11 +43,6 @@ import java.util.Objects; import java.util.Optional; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; - import org.slf4j.Logger; import com.fasterxml.jackson.core.JsonParseException; @@ -55,6 +50,10 @@ import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.google.errorprone.annotations.concurrent.GuardedBy; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import uk.ac.manchester.spinnaker.alloc.model.PowerState; import uk.ac.manchester.spinnaker.machine.ValidP; import uk.ac.manchester.spinnaker.machine.ValidX; @@ -162,23 +161,31 @@ final void handleConnection() { } catch (InterruptedException | InterruptedIOException interrupted) { log.debug("interrupted", interrupted); } catch (IOException e) { - log.error("problem with socket {}", sock, e); + if (!e.getMessage().equals("Pipe closed")) { + log.error("problem with socket {}", sock, e); + } } finally { - log.debug("closing down connection from {}", sock); - closeNotifiers(); - try { - if (nonNull(sock)) { - sock.close(); - } else { - in.close(); - synchronized (out) { - out.close(); - } + shutdown(); + } + } + + private void shutdown() { + var origin = nonNull(sock) ? sock : "TEST"; + log.debug("closing down connection from {}", origin); + closeNotifiers(); + try { + if (nonNull(sock)) { + sock.close(); + } else { + in.close(); + synchronized (out) { + out.close(); } - } catch (IOException e) { - log.error("problem closing socket {}", sock, e); } + } catch (IOException e) { + log.error("problem closing connection {}", origin, e); } + log.debug("closed connection from {}", origin); } /** @@ -258,13 +265,18 @@ private Optional readMessage() * than matching the exception message. You'd think that you'd get * something better, but no... */ - switch (e.getMessage()) { - case "Connection reset": - case "Connection timed out (Read failed)": - return Optional.empty(); - default: - throw e; - } + return switch (e.getMessage()) { + case "Connection reset", "Connection timed out (Read failed)" -> + Optional.empty(); + default -> throw e; + }; + } catch (InterruptedIOException e) { + var ex = new InterruptedException(); + ex.initCause(e); + throw ex; + } + if (currentThread().isInterrupted()) { + throw new InterruptedException(); } if (currentThread().isInterrupted()) { throw new InterruptedException(); @@ -438,73 +450,76 @@ private Object callOperation(Command cmd) throws Exception { log.debug("calling operation '{}'", cmd.getCommand()); var args = cmd.getArgs(); var kwargs = cmd.getKwargs(); - switch (cmd.getCommand()) { - case "create_job": + return switch (cmd.getCommand()) { + case "create_job" -> { // This is three operations really, and an optional parameter. byte[] serialCmd = getJsonMapper().writeValueAsBytes(cmd); - switch (args.size()) { - case 0: - return createJobNumBoards(1, kwargs, serialCmd).orElse(null); - case 1: - return createJobNumBoards(parseDec(args, 0), kwargs, serialCmd) - .orElse(null); - case 2: - return createJobRectangle(parseDec(args, 0), parseDec(args, 1), - kwargs, serialCmd).orElse(null); - case TRIAD_COORD_COUNT: - return createJobSpecificBoard(new TriadCoords(parseDec(args, 0), + // Checkstyle bug: indentation confusion + // CHECKSTYLE:OFF + yield switch (args.size()) { + case 0 -> createJobNumBoards(1, kwargs, serialCmd).orElse(null); + case 1 -> createJobNumBoards(parseDec(args, 0), kwargs, serialCmd) + .orElse(null); + case 2 -> createJobRectangle(parseDec(args, 0), parseDec(args, 1), + kwargs, serialCmd).orElse(null); + case TRIAD_COORD_COUNT -> + createJobSpecificBoard(new TriadCoords(parseDec(args, 0), parseDec(args, 1), parseDec(args, 2)), kwargs, serialCmd).orElse(null); - default: - throw new Oops( - "unsupported number of arguments: " + args.size()); - } - case "destroy_job": + default -> throw new Oops( + "unsupported number of arguments: " + args.size()); + }; + // CHECKSTYLE:ON + } + case "destroy_job" -> { destroyJob(parseDec(args, 0), (String) kwargs.get("reason")); - break; - case "get_board_at_position": - return requireNonNull(getBoardAtPhysicalPosition( - (String) kwargs.get("machine_name"), parseDec(kwargs, "x"), - parseDec(kwargs, "y"), parseDec(kwargs, "z"))); - case "get_board_position": - return requireNonNull(getBoardAtLogicalPosition( + yield null; + } + case "get_board_at_position" -> + requireNonNull(getBoardAtPhysicalPosition( (String) kwargs.get("machine_name"), parseDec(kwargs, "x"), parseDec(kwargs, "y"), parseDec(kwargs, "z"))); - case "get_job_machine_info": - return requireNonNull(getJobMachineInfo(parseDec(args, 0))); - case "get_job_state": - return requireNonNull(getJobState(parseDec(args, 0))); - case "job_keepalive": + case "get_board_position" -> requireNonNull(getBoardAtLogicalPosition( + (String) kwargs.get("machine_name"), parseDec(kwargs, "x"), + parseDec(kwargs, "y"), parseDec(kwargs, "z"))); + case "get_job_machine_info" -> + requireNonNull(getJobMachineInfo(parseDec(args, 0))); + case "get_job_state" -> requireNonNull(getJobState(parseDec(args, 0))); + case "job_keepalive" -> { jobKeepalive(parseDec(args, 0)); - break; - case "list_jobs": - return requireNonNull(listJobs()); - case "list_machines": - return requireNonNull(listMachines()); - case "no_notify_job": + yield null; + } + case "list_jobs" -> requireNonNull(listJobs()); + case "list_machines" -> requireNonNull(listMachines()); + case "no_notify_job" -> { notifyJob(optInt(args), false); - break; - case "no_notify_machine": + yield null; + } + case "no_notify_machine" -> { notifyMachine(optStr(args), false); - break; - case "notify_job": + yield null; + } + case "notify_job" -> { notifyJob(optInt(args), true); - break; - case "notify_machine": + yield null; + } + case "notify_machine" -> { notifyMachine(optStr(args), true); - break; - case "power_off_job_boards": + yield null; + } + case "power_off_job_boards" -> { powerJobBoards(parseDec(args, 0), OFF); - break; - case "power_on_job_boards": + yield null; + } + case "power_on_job_boards" -> { powerJobBoards(parseDec(args, 0), ON); - break; - case "version": - return requireNonNull(version()); - case "where_is": + yield null; + } + case "version" -> requireNonNull(version()); + case "where_is" -> { // This is four operations in a trench coat if (kwargs.containsKey("job_id")) { - return requireNonNull(whereIsJobChip(parseDec(kwargs, "job_id"), + yield requireNonNull(whereIsJobChip(parseDec(kwargs, "job_id"), parseDec(kwargs, "chip_x"), parseDec(kwargs, "chip_y"))); } else if (!kwargs.containsKey("machine")) { @@ -512,21 +527,22 @@ private Object callOperation(Command cmd) throws Exception { } var m = (String) kwargs.get("machine"); if (kwargs.containsKey("chip_x")) { - return requireNonNull( + yield requireNonNull( whereIsMachineChip(m, parseDec(kwargs, "chip_x"), parseDec(kwargs, "chip_y"))); } else if (kwargs.containsKey("x")) { - return requireNonNull( + yield requireNonNull( whereIsMachineLogicalBoard(m, parseDec(kwargs, "x"), parseDec(kwargs, "y"), parseDec(kwargs, "z"))); } else if (kwargs.containsKey("cabinet")) { - return requireNonNull(whereIsMachinePhysicalBoard(m, + yield requireNonNull(whereIsMachinePhysicalBoard(m, parseDec(kwargs, "cabinet"), parseDec(kwargs, "frame"), parseDec(kwargs, "board"))); } else { throw new Oops("missing parameter: chip_x, x, or cabinet"); } - case "report_problem": + } + case "report_problem" -> { var ip = args.get(0).toString(); Integer x = null, y = null, p = null; var desc = "It doesn't work and I don't know why."; @@ -541,13 +557,11 @@ private Object callOperation(Command cmd) throws Exception { desc = Objects.toString(kwargs.get("description")); } reportProblem(ip, x, y, p, desc); - break; - case "login": - throw new Oops("upgrading security is not supported"); - default: - throw new Oops("unknown command: " + cmd.getCommand()); + yield null; } - return null; + case "login" -> throw new Oops("upgrading security is not supported"); + default -> throw new Oops("unknown command: " + cmd.getCommand()); + }; } /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1TaskImpl.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1TaskImpl.java index bb2cdd557b..fe6b87d525 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1TaskImpl.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/compat/V1TaskImpl.java @@ -47,13 +47,12 @@ import java.util.Optional; import java.util.concurrent.Future; -import javax.annotation.PostConstruct; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Component; +import jakarta.annotation.PostConstruct; import uk.ac.manchester.spinnaker.alloc.SpallocProperties; import uk.ac.manchester.spinnaker.alloc.allocator.Epochs; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI; @@ -92,7 +91,6 @@ @Component @Prototype class V1TaskImpl extends V1CompatTask { - /** * We are compatible with spalloc-server release version 5.0.0. */ @@ -185,7 +183,7 @@ protected final JobMachineInfo getJobMachineInfo(int jobId) } } return new JobMachineInfo(w, h, machine.getConnections().stream() - .map(ci -> new Connection(ci.getChip(), ci.getHostname())) + .map(ci -> new Connection(ci.chip(), ci.hostname())) .collect(toList()), machine.getMachine().getName(), machine.getBoards()); } @@ -246,12 +244,12 @@ private Duration parseKeepalive(Number keepalive) { */ private static List tags(Object src, boolean mayForceDefault) { var vals = new ArrayList(); - if (src instanceof List) { - for (var o : (List) src) { + if (src instanceof List lst) { + for (var o : lst) { vals.add(Objects.toString(o)); } - } else if (src instanceof String) { - vals.add((String) src); + } else if (src instanceof String s) { + vals.add(s); } if (vals.isEmpty() && mayForceDefault) { return List.of("default"); @@ -277,7 +275,8 @@ protected final Optional createJobRectangle(int width, int height, @Override protected final Optional createJobSpecificBoard(TriadCoords coords, Map kwargs, byte[] cmd) throws TaskException { - return createJob(triad(coords.x, coords.y, coords.z), kwargs, cmd); + return createJob(triad(coords.x(), coords.y(), coords.z()), kwargs, + cmd); } private static String getOwner(Map kwargs) @@ -370,7 +369,7 @@ private static JobDescription buildJobDescription(V1TaskImpl task, jd.setArgs(args); jd.setKwargs(cmd.getKwargs()); // Override shrouded owner from above - Object owner = cmd.getKwargs().get("owner"); + var owner = cmd.getKwargs().get("owner"); if (owner != null) { jd.setOwner(owner.toString()); } @@ -378,7 +377,7 @@ private static JobDescription buildJobDescription(V1TaskImpl task, jd.setMachine(job.getMachineName()); jd.setPower(job.isPowered()); jd.setBoards(job.getBoards().stream().map( - b -> new BoardCoordinates(b.getX(), b.getY(), b.getZ())) + b -> new BoardCoordinates(b.x(), b.y(), b.z())) .collect(toList())); return jd.build(); } @@ -470,7 +469,7 @@ protected final void notifyMachine(String machineName, boolean wantNotify) try { var changed = epoch.getChanged( mainProps.getCompat().getNotifyWaitTime()); - return changed.stream().map(id -> invMap.get(id)) + return changed.stream().map(invMap::get) .collect(toList()); } catch (InterruptedException e) { return List.of(); diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/DataAccessExceptionMapper.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/DataAccessExceptionMapper.java index d471a8a6e0..c8fd149799 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/DataAccessExceptionMapper.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/DataAccessExceptionMapper.java @@ -15,21 +15,20 @@ */ package uk.ac.manchester.spinnaker.alloc.db; -import static javax.ws.rs.core.Response.status; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static jakarta.ws.rs.core.Response.status; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.beans.factory.config.BeanDefinition.ROLE_SUPPORT; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Role; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Component; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import uk.ac.manchester.spinnaker.alloc.SpallocProperties; /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/DatabaseEngineJDBCImpl.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/DatabaseEngineJDBCImpl.java index 562fb53c0d..3a58f0c309 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/DatabaseEngineJDBCImpl.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/DatabaseEngineJDBCImpl.java @@ -32,8 +32,6 @@ import java.util.List; import java.util.Optional; -import javax.annotation.PostConstruct; - import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -51,6 +49,8 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; +import jakarta.annotation.PostConstruct; + /** * Implementation of the {@link DatabaseAPI} that uses JDBC to talk to MySQL. */ @@ -256,16 +256,16 @@ final Object[] resolveArguments(Object[] arguments) { for (int i = 0; i < arguments.length; i++) { var arg = arguments[i]; // The classes we augment the DB driver with - if (arg instanceof Optional) { + if (arg instanceof Optional opt) { // Unpack one layer of Optional only; absent = NULL - arg = ((Optional) arg).orElse(null); + arg = opt.orElse(null); } - if (arg instanceof Instant) { - arg = ((Instant) arg).getEpochSecond(); - } else if (arg instanceof Duration) { - arg = ((Duration) arg).getSeconds(); - } else if (arg instanceof Enum) { - arg = ((Enum) arg).ordinal(); + if (arg instanceof Instant inst) { + arg = inst.getEpochSecond(); + } else if (arg instanceof Duration d) { + arg = d.getSeconds(); + } else if (arg instanceof Enum e) { + arg = e.ordinal(); } else if (arg != null && arg instanceof Serializable && !(arg instanceof String || arg instanceof Number || arg instanceof Boolean @@ -363,8 +363,7 @@ public Optional key(Object... arguments) { */ private String readSQL(Resource resource) { try (var is = resource.getInputStream()) { - var s = IOUtils.toString(is, UTF_8); - return s; + return IOUtils.toString(is, UTF_8); } catch (IOException e) { throw new UncategorizedScriptException( "could not load SQL file from " + resource, e); diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/Row.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/Row.java index 28fd9b4e5a..40b885313c 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/Row.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/Row.java @@ -211,11 +211,11 @@ public static ToIntFunction int32(String columnLabel) { public Integer getInteger(String columnLabel) { return get(() -> { var obj = rs.getObject(columnLabel); - if (obj instanceof Long) { - return ((Long) obj).intValue(); + if (obj instanceof Long l) { + return l.intValue(); } - if (obj instanceof BigDecimal) { - return ((BigDecimal) obj).intValue(); + if (obj instanceof BigDecimal bd) { + return bd.intValue(); } return (Integer) obj; }); @@ -545,6 +545,6 @@ public static RowMapper core(String x, String y, String p) { * @return A mappable iterator. */ public static MappableIterable stream(List lst) { - return () -> lst.iterator(); + return lst::iterator; } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/SQLExceptionMapper.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/SQLExceptionMapper.java index 62982ecb5e..5e14ab06d2 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/SQLExceptionMapper.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/SQLExceptionMapper.java @@ -15,20 +15,19 @@ */ package uk.ac.manchester.spinnaker.alloc.db; -import static javax.ws.rs.core.Response.status; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static jakarta.ws.rs.core.Response.status; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.slf4j.LoggerFactory.getLogger; import java.sql.SQLException; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import uk.ac.manchester.spinnaker.alloc.SpallocProperties; /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/SQLQueries.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/SQLQueries.java index 6dde35fa35..57e7c3ef40 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/SQLQueries.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/SQLQueries.java @@ -60,20 +60,22 @@ public abstract class SQLQueries { @ResultColumn("width") @ResultColumn("height") @ResultColumn("in_service") - protected static final String GET_ALL_MACHINES = - "SELECT machine_id, machine_name, width, height, in_service " - + "FROM machines " - + "WHERE in_service OR :allow_out_of_service " - + "ORDER BY machine_name ASC"; + protected static final String GET_ALL_MACHINES = """ + SELECT machine_id, machine_name, width, height, in_service + FROM machines + WHERE in_service OR :allow_out_of_service + ORDER BY machine_name ASC + """; /** Get the machine names in alphabetical order. */ @Parameter("allow_out_of_service") @ResultColumn("machine_name") @ResultColumn("in_service") - protected static final String LIST_MACHINE_NAMES = - "SELECT machine_name, in_service FROM machines " - + "WHERE in_service OR :allow_out_of_service " - + "ORDER BY machine_name ASC"; + protected static final String LIST_MACHINE_NAMES = """ + SELECT machine_name, in_service FROM machines + WHERE in_service OR :allow_out_of_service + ORDER BY machine_name ASC + """; /** Get basic information about a specific machine. Looks up by ID. */ @Parameter("machine_id") @@ -84,10 +86,13 @@ public abstract class SQLQueries { @ResultColumn("height") @ResultColumn("in_service") @SingleRowResult - protected static final String GET_MACHINE_BY_ID = - "SELECT machine_id, machine_name, width, height, in_service " - + "FROM machines WHERE machine_id = :machine_id " - + "AND (in_service OR :allow_out_of_service) LIMIT 1"; + protected static final String GET_MACHINE_BY_ID = """ + SELECT machine_id, machine_name, width, height, in_service + FROM machines + WHERE machine_id = :machine_id + AND (in_service OR :allow_out_of_service) + LIMIT 1 + """; /** Get basic information about a specific machine. Looks up by name. */ @Parameter("machine_name") @@ -98,10 +103,13 @@ public abstract class SQLQueries { @ResultColumn("height") @ResultColumn("in_service") @SingleRowResult - protected static final String GET_NAMED_MACHINE = - "SELECT machine_id, machine_name, width, height, in_service " - + "FROM machines WHERE machine_name = :machine_name " - + "AND (in_service OR :allow_out_of_service) LIMIT 1"; + protected static final String GET_NAMED_MACHINE = """ + SELECT machine_id, machine_name, width, height, in_service + FROM machines + WHERE machine_name = :machine_name + AND (in_service OR :allow_out_of_service) + LIMIT 1 + """; /** * Get whether a machine wraps in horizontal or vertical directions. @@ -111,24 +119,30 @@ public abstract class SQLQueries { @ResultColumn("horizontal_wrap") @ResultColumn("vertical_wrap") @SingleRowResult - protected static final String GET_MACHINE_WRAPS = - "WITH linked AS (" - + "SELECT b1.machine_id, b1.x AS x1, b1.y AS y1, " - + "b2.x AS x2, b2.y AS y2 FROM links " - + "JOIN boards AS b1 ON links.board_1 = b1.board_id " - + "JOIN boards AS b2 ON links.board_2 = b2.board_id) " - + "SELECT " - + "EXISTS (SELECT 1 FROM linked WHERE " - + "linked.machine_id = machines.machine_id AND " - + "((linked.x1 = 0 AND linked.x2 = machines.width - 1) OR " - + "(linked.x2 = 0 AND linked.x1 = machines.width - 1))) " - + "AS horizontal_wrap, " - + "EXISTS (SELECT 1 FROM linked WHERE " - + "linked.machine_id = machines.machine_id AND " - + "((linked.y1 = 0 AND linked.y2 = machines.height - 1) OR " - + "(linked.y2 = 0 AND linked.y1 = machines.height - 1))) " - + "AS vertical_wrap " - + "FROM machines WHERE machine_id = :machine_id LIMIT 1"; + protected static final String GET_MACHINE_WRAPS = """ + WITH linked AS ( + SELECT b1.machine_id, b1.x AS x1, b1.y AS y1, + b2.x AS x2, b2.y AS y2 + FROM links + JOIN boards AS b1 ON links.board_1 = b1.board_id + JOIN boards AS b2 ON links.board_2 = b2.board_id) + SELECT + EXISTS ( + SELECT 1 FROM linked + WHERE linked.machine_id = machines.machine_id + AND ((linked.x1 = 0 AND linked.x2 = machines.width - 1) + OR (linked.x2 = 0 AND linked.x1 = machines.width - 1)) + ) AS horizontal_wrap, + EXISTS ( + SELECT 1 FROM linked + WHERE linked.machine_id = machines.machine_id + AND ((linked.y1 = 0 AND linked.y2 = machines.height - 1) + OR (linked.y2 = 0 AND linked.y1 = machines.height - 1)) + ) AS vertical_wrap + FROM machines + WHERE machine_id = :machine_id + LIMIT 1 + """; /** Count things on a machine. */ @Parameter("machine_id") @@ -136,16 +150,18 @@ public abstract class SQLQueries { @ResultColumn("in_use") @ResultColumn("num_jobs") @SingleRowResult - protected static final String COUNT_MACHINE_THINGS = - "WITH args(m) AS (SELECT :machine_id), " - + "b AS (SELECT * from boards,args WHERE machine_id = m), " - + "bc AS (SELECT COUNT(*) AS c FROM b), " - + "iu AS (SELECT COUNT(*) AS c FROM b " - + "WHERE allocated_job IS NOT NULL), " - + "jc AS (SELECT COUNT(*) AS c FROM jobs,args " - + "WHERE machine_id = m AND job_state != 4) " // DESTROYED - + "SELECT bc.c AS board_count, iu.c AS in_use, " - + "jc.c AS num_jobs FROM bc, iu, jc"; + protected static final String COUNT_MACHINE_THINGS = """ + WITH args(m) AS (SELECT :machine_id), + b AS (SELECT * from boards, args WHERE machine_id = m), + bc AS (SELECT COUNT(*) AS c FROM b), + iu AS (SELECT COUNT(*) AS c FROM b + WHERE allocated_job IS NOT NULL), + jc AS (SELECT COUNT(*) AS c FROM jobs,args + WHERE machine_id = m + AND job_state != 4) -- job is not DESTROYED + SELECT bc.c AS board_count, iu.c AS in_use, + jc.c AS num_jobs FROM bc, iu, jc + """; /** Get basic information about jobs. Supports paging. */ @Parameter("limit") @@ -154,10 +170,12 @@ public abstract class SQLQueries { @ResultColumn("machine_id") @ResultColumn("job_state") @ResultColumn("keepalive_timestamp") - protected static final String GET_JOB_IDS = - "SELECT job_id, machine_id, job_state, keepalive_timestamp " - + "FROM jobs ORDER BY job_id DESC " - + "LIMIT :limit OFFSET :offset"; + protected static final String GET_JOB_IDS = """ + SELECT job_id, machine_id, job_state, keepalive_timestamp + FROM jobs + ORDER BY job_id DESC + LIMIT :limit OFFSET :offset + """; /** Get basic information about live jobs. Supports paging. */ @Parameter("limit") @@ -166,10 +184,13 @@ public abstract class SQLQueries { @ResultColumn("machine_id") @ResultColumn("job_state") @ResultColumn("keepalive_timestamp") - protected static final String GET_LIVE_JOB_IDS = - "SELECT job_id, machine_id, job_state, keepalive_timestamp " - + "FROM jobs WHERE job_state != 4 " // DESTROYED - + "ORDER BY job_id DESC LIMIT :limit OFFSET :offset"; + protected static final String GET_LIVE_JOB_IDS = """ + SELECT job_id, machine_id, job_state, keepalive_timestamp + FROM jobs + WHERE job_state != 4 -- job is not DESTROYED + ORDER BY job_id DESC + LIMIT :limit OFFSET :offset + """; /** Get basic information about a specific job. */ @Parameter("job_id") @@ -190,40 +211,48 @@ public abstract class SQLQueries { @ResultColumn("original_request") @ResultColumn("owner") @SingleRowResult - protected static final String GET_JOB = - "SELECT job_id, jobs.machine_id, machines.machine_name, " - + "jobs.width, jobs.height, jobs.depth, " - + "root_id, job_state, keepalive_timestamp, " - + "keepalive_host, keepalive_interval, create_timestamp, " - + "death_reason, death_timestamp, original_request, " - + "user_info.user_name AS owner FROM jobs " - + "JOIN user_info ON jobs.owner = user_info.user_id " - + "JOIN machines USING (machine_id) " - + "WHERE job_id = :job_id LIMIT 1"; + protected static final String GET_JOB = """ + SELECT job_id, jobs.machine_id, machines.machine_name, + jobs.width, jobs.height, jobs.depth, + root_id, job_state, keepalive_timestamp, + keepalive_host, keepalive_interval, create_timestamp, + death_reason, death_timestamp, original_request, + user_info.user_name AS owner + FROM jobs + JOIN user_info ON jobs.owner = user_info.user_id + JOIN machines USING (machine_id) + WHERE job_id = :job_id + LIMIT 1 + """; /** Get the chip dimensions of a job. */ @Parameter("job_id") @ResultColumn("width") @ResultColumn("height") @SingleRowResult - protected static final String GET_JOB_CHIP_DIMENSIONS = - "WITH b AS (SELECT * FROM boards WHERE allocated_job = :job_id), " - + "c AS (SELECT root_x + chip_x AS x, root_y + chip_y AS y " - + "FROM b JOIN machines USING (machine_id) " - + "JOIN board_model_coords ON " - + "machines.board_model = board_model_coords.model) " - + "SELECT MAX(x) - MIN(x) + 1 AS width, " - + "MAX(y) - MIN(y) + 1 AS height FROM c LIMIT 1"; + protected static final String GET_JOB_CHIP_DIMENSIONS = """ + WITH b AS (SELECT * FROM boards WHERE allocated_job = :job_id), + c AS (SELECT root_x + chip_x AS x, root_y + chip_y AS y + FROM b JOIN machines USING (machine_id) + JOIN board_model_coords ON + machines.board_model = board_model_coords.model) + SELECT MAX(x) - MIN(x) + 1 AS width, + MAX(y) - MIN(y) + 1 AS height + FROM c + LIMIT 1 + """; /** Get what boards are allocated to a job. */ @Parameter("job_id") @ResultColumn("board_id") @ResultColumn("bmp_id") @ResultColumn("machine_id") - protected static final String GET_JOB_BOARDS = - "SELECT board_id, bmp_id, boards.machine_id FROM boards JOIN jobs " - + "ON boards.allocated_job = jobs.job_id " - + "WHERE boards.allocated_job = :job_id"; + protected static final String GET_JOB_BOARDS = """ + SELECT board_id, bmp_id, boards.machine_id + FROM boards + JOIN jobs ON boards.allocated_job = jobs.job_id + WHERE boards.allocated_job = :job_id + """; /** Gets information about live jobs. */ @ResultColumn("job_id") @@ -236,31 +265,38 @@ public abstract class SQLQueries { @ResultColumn("user_name") @ResultColumn("machine_name") @ResultColumn("original_request") - protected static final String LIST_LIVE_JOBS = - "SELECT job_id, jobs.machine_id, create_timestamp, " - + "keepalive_interval, job_state, allocation_size, " - + "keepalive_host, user_name, machines.machine_name, " - + "original_request " - + "FROM jobs " + "JOIN machines USING (machine_id) " - + "JOIN user_info ON jobs.owner = user_info.user_id " - + "WHERE job_state != 4"; // DESTROYED + protected static final String LIST_LIVE_JOBS = """ + SELECT job_id, jobs.machine_id, create_timestamp, + keepalive_interval, job_state, allocation_size, + keepalive_host, user_name, machines.machine_name, + original_request + FROM jobs + JOIN machines USING (machine_id) + JOIN user_info ON jobs.owner = user_info.user_id + WHERE job_state != 4 -- job is not DESTROYED + """; /** Counts the number of powered-on boards of a job. */ @Parameter("job_id") @ResultColumn("c") @SingleRowResult - protected static final String COUNT_POWERED_BOARDS = - "SELECT COUNT(*) AS c FROM boards " - + "WHERE allocated_job = :job_id AND board_power"; + protected static final String COUNT_POWERED_BOARDS = """ + SELECT COUNT(*) AS c + FROM boards + WHERE allocated_job = :job_id AND board_power + """; /** Get the coordinates of the root chip of a board. */ @Parameter("board_id") @ResultColumn("root_x") @ResultColumn("root_y") @SingleRowResult - protected static final String GET_ROOT_OF_BOARD = - "SELECT root_x, root_y FROM boards WHERE board_id = :board_id " - + "LIMIT 1"; + protected static final String GET_ROOT_OF_BOARD = """ + SELECT root_x, root_y + FROM boards + WHERE board_id = :board_id + LIMIT 1 + """; /** Create a job. */ @Parameter("machine_id") @@ -269,13 +305,15 @@ public abstract class SQLQueries { @Parameter("keepalive_interval") @Parameter("original_request") @GeneratesID - protected static final String INSERT_JOB = "INSERT INTO jobs(" - + "machine_id, owner, group_id, keepalive_interval, " - + "original_request, keepalive_timestamp, create_timestamp, " - + "job_state) " - + "VALUES(:machine_id, :user_id, :group_id, :keepalive_interval, " - + ":original_request, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), " - + /* QUEUED */ "1)"; + protected static final String INSERT_JOB = """ + INSERT INTO jobs( + machine_id, owner, group_id, keepalive_interval, + original_request, keepalive_timestamp, create_timestamp, + job_state) + VALUES(:machine_id, :user_id, :group_id, :keepalive_interval, + :original_request, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), + 1) -- job starts QUEUED + """; /** Create a request to allocate a number of boards. */ @Parameter("job_id") @@ -283,11 +321,11 @@ public abstract class SQLQueries { @Parameter("max_dead_boards") @Parameter("priority") @GeneratesID - protected static final String INSERT_REQ_N_BOARDS = - "INSERT INTO job_request(job_id, num_boards, max_dead_boards, " - + "priority) " - + "VALUES (:job_id, :num_boards, :max_dead_boards, " - + ":priority)"; + protected static final String INSERT_REQ_N_BOARDS = """ + INSERT INTO job_request( + job_id, num_boards, max_dead_boards, priority) + VALUES (:job_id, :num_boards, :max_dead_boards, :priority) + """; /** Create a request to allocate a rectangle of boards. */ @Parameter("job_id") @@ -296,20 +334,22 @@ public abstract class SQLQueries { @Parameter("max_dead_boards") @Parameter("priority") @GeneratesID - protected static final String INSERT_REQ_SIZE = - "INSERT INTO job_request(job_id, width, height, max_dead_boards, " - + "priority) " - + "VALUES (:job_id, :width, :height, :max_dead_boards, " - + ":priority)"; + protected static final String INSERT_REQ_SIZE = """ + INSERT INTO job_request( + job_id, width, height, max_dead_boards, priority) + VALUES (:job_id, :width, :height, :max_dead_boards, :priority) + """; /** Create a request to allocate a specific board. */ @Parameter("job_id") @Parameter("board_id") @Parameter("priority") @GeneratesID - protected static final String INSERT_REQ_BOARD = - "INSERT INTO job_request(job_id, board_id, priority) " - + "VALUES (:job_id, :board_id, :priority)"; + protected static final String INSERT_REQ_BOARD = """ + INSERT INTO job_request( + job_id, board_id, priority) + VALUES (:job_id, :board_id, :priority) + """; /** Create a request to allocate triads starting at a particular board. */ @Parameter("job_id") @@ -319,24 +359,31 @@ public abstract class SQLQueries { @Parameter("max_dead_boards") @Parameter("priority") @GeneratesID - protected static final String INSERT_REQ_SIZE_BOARD = "INSERT INTO " - + "job_request(job_id, board_id, width, height, max_dead_boards, " - + "priority) " - + "VALUES(:job_id, :board_id, :width, :height, :max_dead_boards," - + ":priority)"; - - /** Increases the importance of a job. */ - protected static final String BUMP_IMPORTANCE = - "UPDATE job_request SET importance = importance + priority"; + protected static final String INSERT_REQ_SIZE_BOARD = """ + INSERT INTO job_request( + job_id, board_id, width, height, max_dead_boards, priority) + VALUES (:job_id, :board_id, :width, :height, :max_dead_boards, + :priority) + """; + + /** Increases the importance of all current job allocation requests. */ + protected static final String BUMP_IMPORTANCE = """ + UPDATE job_request + SET importance = importance + priority + """; /** Get the address of the BMP of the root board of the machine. */ @Parameter("machine_id") @ResultColumn("address") @SingleRowResult - protected static final String GET_ROOT_BMP_ADDRESS = - "SELECT bmp.address FROM bmp JOIN boards USING (bmp_id) " - + "WHERE boards.machine_id = :machine_id " - + "AND boards.x = 0 AND boards.y = 0 LIMIT 1"; + protected static final String GET_ROOT_BMP_ADDRESS = """ + SELECT bmp.address + FROM bmp + JOIN boards USING (bmp_id) + WHERE boards.machine_id = :machine_id + AND boards.x = 0 AND boards.y = 0 + LIMIT 1 + """; /** Get the address of the BMP of the root board of the machine. */ @Parameter("machine_id") @@ -344,9 +391,13 @@ public abstract class SQLQueries { @Parameter("frame") @ResultColumn("address") @SingleRowResult - protected static final String GET_BMP_ADDRESS = - "SELECT address FROM bmp WHERE machine_id = :machine_id " - + "AND cabinet = :cabinet AND frame = :frame LIMIT 1"; + protected static final String GET_BMP_ADDRESS = """ + SELECT address + FROM bmp + WHERE machine_id = :machine_id + AND cabinet = :cabinet AND frame = :frame + LIMIT 1 + """; /** Get all the BMPs. */ @ResultColumn("bmp_id") @@ -354,36 +405,47 @@ public abstract class SQLQueries { @ResultColumn("address") @ResultColumn("cabinet") @ResultColumn("frame") - protected static final String GET_ALL_BMPS = - "SELECT bmp_id, machine_name, address, cabinet, frame FROM bmp " - + "JOIN machines on bmp.machine_id = machines.machine_id"; + protected static final String GET_ALL_BMPS = """ + SELECT bmp_id, machine_name, address, cabinet, frame + FROM bmp + JOIN machines on bmp.machine_id = machines.machine_id + """; /** Get all the boards of a BMP. */ @Parameter("bmp_id") @ResultColumn("board_id") @ResultColumn("board_num") @ResultColumn("address") - protected static final String GET_ALL_BMP_BOARDS = - "SELECT board_id, board_num, address FROM boards " - + "WHERE bmp_id = :bmp_id"; + protected static final String GET_ALL_BMP_BOARDS = """ + SELECT board_id, board_num, address + FROM boards + WHERE bmp_id = :bmp_id + """; /** Get the address of the root chip of a board. */ @Parameter("board_id") @ResultColumn("address") @SingleRowResult - protected static final String GET_BOARD_ADDRESS = - "SELECT address FROM boards WHERE board_id = :board_id LIMIT 1"; + protected static final String GET_BOARD_ADDRESS = """ + SELECT address + FROM boards + WHERE board_id = :board_id + LIMIT 1 + """; /** * Get the boards of a machine that can be used. Excludes disabled boards. */ @Parameter("machine_id") @ResultColumn("board_num") - protected static final String GET_BOARD_NUMBERS = - "SELECT board_num FROM boards WHERE machine_id = :machine_id " - + "AND board_num IS NOT NULL " - + "AND (functioning IS NULL OR functioning != 0) " - + "ORDER BY board_num ASC"; + protected static final String GET_BOARD_NUMBERS = """ + SELECT board_num + FROM boards + WHERE machine_id = :machine_id + AND board_num IS NOT NULL + AND (functioning IS NULL OR functioning != 0) + ORDER BY board_num ASC + """; /** * Get the boards of a BMP that can be used. Excludes disabled boards. @@ -392,13 +454,16 @@ public abstract class SQLQueries { @Parameter("cabinet") @Parameter("frame") @ResultColumn("board_num") - protected static final String GET_BMP_BOARD_NUMBERS = - "SELECT board_num FROM boards JOIN bmp USING (bmp_id) " - + "WHERE boards.machine_id = :machine_id " - + "AND cabinet = :cabinet AND frame = :frame " - + "AND board_num IS NOT NULL " - + "AND (functioning IS NULL OR functioning != 0) " - + "ORDER BY board_num ASC"; + protected static final String GET_BMP_BOARD_NUMBERS = """ + SELECT board_num + FROM boards + JOIN bmp USING (bmp_id) + WHERE boards.machine_id = :machine_id + AND cabinet = :cabinet AND frame = :frame + AND board_num IS NOT NULL + AND (functioning IS NULL OR functioning != 0) + ORDER BY board_num ASC + """; /** * Get the boards (and related info) of a machine that are in service. @@ -414,13 +479,16 @@ public abstract class SQLQueries { @ResultColumn("address") @ResultColumn("bmp_id") @ResultColumn("machine_id") - protected static final String GET_LIVE_BOARDS = - "SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, " - + "boards.address, bmp_id, boards.machine_id FROM boards " - + "JOIN bmp USING (bmp_id) " - + "WHERE boards.machine_id = :machine_id " - + "AND board_num IS NOT NULL " - + "AND functioning = 1 ORDER BY z ASC, x ASC, y ASC"; + protected static final String GET_LIVE_BOARDS = """ + SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, + boards.address, bmp_id, boards.machine_id + FROM boards + JOIN bmp USING (bmp_id) + WHERE boards.machine_id = :machine_id + AND board_num IS NOT NULL + AND functioning = 1 + ORDER BY z ASC, x ASC, y ASC + """; /** * Get the boards (and related info) of a machine that have been disabled. @@ -436,13 +504,15 @@ public abstract class SQLQueries { @ResultColumn("address") @ResultColumn("bmp_id") @ResultColumn("machine_id") - protected static final String GET_DEAD_BOARDS = - "SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, " - + "boards.address, bmp_id, boards.machine_id FROM boards " - + "JOIN bmp USING (bmp_id) " - + "WHERE boards.machine_id = :machine_id " - + "AND (board_num IS NULL OR functioning = 0) " - + "ORDER BY z ASC, x ASC, y ASC"; + protected static final String GET_DEAD_BOARDS = """ + SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, + boards.address, bmp_id, boards.machine_id + FROM boards + JOIN bmp USING (bmp_id) + WHERE boards.machine_id = :machine_id + AND (board_num IS NULL OR functioning = 0) + ORDER BY z ASC, x ASC, y ASC + """; /** * Get all the boards (and related info) of a machine. @@ -460,13 +530,15 @@ public abstract class SQLQueries { @ResultColumn("address") @ResultColumn("bmp_id") @ResultColumn("machine_id") - protected static final String GET_ALL_BOARDS = - "SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, " - + "boards.address, bmp_id, boards.machine_id FROM boards " - + "JOIN bmp USING (bmp_id) " - + "WHERE boards.machine_id = :machine_id " - + "AND board_num IS NOT NULL " - + "ORDER BY z ASC, x ASC, y ASC"; + protected static final String GET_ALL_BOARDS = """ + SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, + boards.address, bmp_id, boards.machine_id + FROM boards + JOIN bmp USING (bmp_id) + WHERE boards.machine_id = :machine_id + AND board_num IS NOT NULL + ORDER BY z ASC, x ASC, y ASC + """; /** * Get all the boards (and related info) known to the service. @@ -483,12 +555,14 @@ public abstract class SQLQueries { @ResultColumn("address") @ResultColumn("bmp_id") @ResultColumn("machine_id") - protected static final String GET_ALL_BOARDS_OF_ALL_MACHINES = - "SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, " - + "boards.address, bmp_id, boards.machine_id FROM boards " - + "JOIN bmp USING (bmp_id) " - + "WHERE board_num IS NOT NULL " - + "ORDER BY z ASC, x ASC, y ASC"; + protected static final String GET_ALL_BOARDS_OF_ALL_MACHINES = """ + SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, + boards.address, bmp_id, boards.machine_id + FROM boards + JOIN bmp USING (bmp_id) + WHERE board_num IS NOT NULL + ORDER BY z ASC, x ASC, y ASC + """; /** * Get the coords of boards assigned to a job. @@ -504,31 +578,36 @@ public abstract class SQLQueries { @ResultColumn("address") @ResultColumn("bmp_id") @ResultColumn("machine_id") - protected static final String GET_JOB_BOARD_COORDS = - "SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, " - + "boards.address, bmp_id, boards.machine_id FROM boards " - + "JOIN bmp USING (bmp_id) " - + "WHERE boards.allocated_job = :job_id " - + "ORDER BY z ASC, x ASC, y ASC"; + protected static final String GET_JOB_BOARD_COORDS = """ + SELECT board_id, x, y, z, bmp.cabinet, bmp.frame, board_num, + boards.address, bmp_id, boards.machine_id + FROM boards + JOIN bmp USING (bmp_id) + WHERE boards.allocated_job = :job_id + ORDER BY z ASC, x ASC, y ASC + """; /** Get basic info about active jobs on a machine. */ @Parameter("machine_id") @ResultColumn("job_id") @ResultColumn("owner_name") - protected static final String GET_MACHINE_JOBS = - "SELECT job_id, user_info.user_name AS owner_name FROM jobs " - + "JOIN user_info ON jobs.owner = user_info.user_id " - + "WHERE machine_id = :machine_id AND job_state != 4 " - // job is not DESTROYED - + "ORDER BY job_id ASC"; + protected static final String GET_MACHINE_JOBS = """ + SELECT job_id, user_info.user_name AS owner_name + FROM jobs + JOIN user_info ON jobs.owner = user_info.user_id + WHERE machine_id = :machine_id + AND job_state != 4 -- job is not DESTROYED + ORDER BY job_id ASC + """; /** Get the boards that are available for allocation. */ @Parameter("machine_id") @ResultColumn("board_num") - protected static final String GET_AVAILABLE_BOARD_NUMBERS = - "SELECT board_num FROM boards " - + "WHERE machine_id = :machine_id AND may_be_allocated " - + "ORDER BY board_num ASC"; + protected static final String GET_AVAILABLE_BOARD_NUMBERS = """ + SELECT board_num FROM boards + WHERE machine_id = :machine_id AND may_be_allocated + ORDER BY board_num ASC + """; /** * Get a machine's tags. Theoretically when selecting a machine by tags we @@ -538,32 +617,41 @@ public abstract class SQLQueries { */ @Parameter("machine_id") @ResultColumn("tag") - protected static final String GET_TAGS = - "SELECT tag FROM tags WHERE machine_id = :machine_id"; + protected static final String GET_TAGS = """ + SELECT tag + FROM tags + WHERE machine_id = :machine_id + """; /** Update the keepalive timestamp. */ @Parameter("keepalive_host") @Parameter("job_id") - protected static final String UPDATE_KEEPALIVE = - "UPDATE jobs SET keepalive_timestamp = UNIX_TIMESTAMP(), " - + "keepalive_host = :keepalive_host WHERE job_id = :job_id " - + "AND job_state != 4"; // DESTROYED + protected static final String UPDATE_KEEPALIVE = """ + UPDATE jobs + SET keepalive_timestamp = UNIX_TIMESTAMP(), + keepalive_host = :keepalive_host + WHERE job_id = :job_id AND job_state != 4 -- job is not DESTROYED + """; /** Mark a job as dead. */ @Parameter("death_reason") @Parameter("job_id") - protected static final String DESTROY_JOB = - "UPDATE jobs SET job_state = 4, death_reason = :death_reason, " - // 4 = DESTROYED - + "death_timestamp = UNIX_TIMESTAMP() " - + "WHERE job_id = :job_id AND job_state != 4"; + protected static final String DESTROY_JOB = """ + UPDATE jobs + SET job_state = 4, -- job becomes DESTROYED + death_reason = :death_reason, + death_timestamp = UNIX_TIMESTAMP() + WHERE job_id = :job_id AND job_state != 4 -- job is not DESTROYED + """; /** Record the reason for a job being destroyed. */ @Parameter("death_reason") @Parameter("job_id") - protected static final String NOTE_DESTROY_REASON = - "UPDATE jobs SET death_reason = :death_reason " - + "WHERE job_id = :job_id"; + protected static final String NOTE_DESTROY_REASON = """ + UPDATE jobs + SET death_reason = :death_reason + WHERE job_id = :job_id + """; /** * Get the number of boards that are allocated to a job that are switched @@ -572,9 +660,11 @@ public abstract class SQLQueries { @Parameter("job_id") @ResultColumn("total_on") @SingleRowResult - protected static final String GET_SUM_BOARDS_POWERED = - "SELECT COALESCE(sum(board_power), 0) AS total_on FROM boards " - + "WHERE allocated_job = :job_id"; + protected static final String GET_SUM_BOARDS_POWERED = """ + SELECT COALESCE(sum(board_power), 0) AS total_on + FROM boards + WHERE allocated_job = :job_id + """; /** Get connection info for board allocated to a job. */ @Parameter("job_id") @@ -585,13 +675,14 @@ public abstract class SQLQueries { @ResultColumn("z") @ResultColumn("root_x") @ResultColumn("root_y") - protected static final String GET_BOARD_CONNECT_INFO = - "SELECT board_id, address, x, y, z, root_x, root_y " - + "FROM boards JOIN jobs " - + "ON boards.allocated_job = jobs.job_id " - + "WHERE allocated_job = :job_id " - + "AND jobs.job_state != 4 " // DESTROYED - + "ORDER BY x ASC, y ASC"; + protected static final String GET_BOARD_CONNECT_INFO = """ + SELECT board_id, address, x, y, z, root_x, root_y + FROM boards + JOIN jobs ON boards.allocated_job = jobs.job_id + WHERE allocated_job = :job_id + AND jobs.job_state != 4 -- job is not DESTROYED + ORDER BY x ASC, y ASC + """; /** Get the coordinates of a board. */ @Parameter("board_id") @@ -601,9 +692,12 @@ public abstract class SQLQueries { @ResultColumn("root_x") @ResultColumn("root_y") @SingleRowResult - protected static final String GET_ROOT_COORDS = - "SELECT x, y, z, root_x, root_y FROM boards " - + "WHERE board_id = :board_id LIMIT 1"; + protected static final String GET_ROOT_COORDS = """ + SELECT x, y, z, root_x, root_y + FROM boards + WHERE board_id = :board_id + LIMIT 1 + """; /** Get whether a board is powered. */ @Parameter("board_id") @@ -611,17 +705,23 @@ public abstract class SQLQueries { @ResultColumn("power_off_timestamp") @ResultColumn("power_on_timestamp") @SingleRowResult - protected static final String GET_BOARD_POWER_INFO = - "SELECT board_power, power_off_timestamp, power_on_timestamp " - + "FROM boards WHERE board_id = :board_id LIMIT 1"; + protected static final String GET_BOARD_POWER_INFO = """ + SELECT board_power, power_off_timestamp, power_on_timestamp + FROM boards + WHERE board_id = :board_id + LIMIT 1 + """; /** Get What job is allocated to a board. */ @Parameter("board_id") @ResultColumn("allocated_job") @SingleRowResult - protected static final String GET_BOARD_JOB = - "SELECT allocated_job FROM boards WHERE board_id = :board_id " - + "LIMIT 1"; + protected static final String GET_BOARD_JOB = """ + SELECT allocated_job + FROM boards + WHERE board_id = :board_id + LIMIT 1 + """; /** * Get the problem reports about boards in a machine. @@ -634,13 +734,15 @@ public abstract class SQLQueries { @ResultColumn("reported_issue") @ResultColumn("report_timestamp") @ResultColumn("reporter_name") - protected static final String GET_MACHINE_REPORTS = - "SELECT board_id, report_id, reported_issue, report_timestamp, " - + "user_name AS reporter_name " - + "FROM board_reports JOIN user_info ON reporter = user_id " - + "JOIN boards USING (board_id) " - + "WHERE machine_id = :machine_id " - + "ORDER BY board_id, report_id"; + protected static final String GET_MACHINE_REPORTS = """ + SELECT board_id, report_id, reported_issue, report_timestamp, + user_name AS reporter_name + FROM board_reports + JOIN user_info ON reporter = user_id + JOIN boards USING (board_id) + WHERE machine_id = :machine_id + ORDER BY board_id, report_id + """; /** * Get the problem reports about a board. @@ -653,16 +755,20 @@ public abstract class SQLQueries { @ResultColumn("reported_issue") @ResultColumn("report_timestamp") @ResultColumn("reporter_name") - protected static final String GET_BOARD_REPORTS = - "SELECT board_id, report_id, reported_issue, report_timestamp, " - + "user_name AS reporter_name " - + "FROM board_reports JOIN user_info ON reporter = user_id " - + "WHERE board_id = :board_id"; + protected static final String GET_BOARD_REPORTS = """ + SELECT board_id, report_id, reported_issue, report_timestamp, + user_name AS reporter_name + FROM board_reports + JOIN user_info ON reporter = user_id + WHERE board_id = :board_id + """; /** Delete an allocation task. */ @Parameter("request_id") - protected static final String DELETE_TASK = - "DELETE FROM job_request WHERE req_id = :request_id"; + protected static final String DELETE_TASK = """ + DELETE FROM job_request + WHERE req_id = :request_id + """; /** Find a single free board. */ @Parameter("machine_id") @@ -670,10 +776,13 @@ public abstract class SQLQueries { @ResultColumn("y") @ResultColumn("z") @SingleRowResult - protected static final String FIND_FREE_BOARD = - "SELECT x, y, z FROM boards " - + "WHERE machine_id = :machine_id AND may_be_allocated " - + "ORDER BY power_off_timestamp ASC LIMIT 1"; + protected static final String FIND_FREE_BOARD = """ + SELECT x, y, z + FROM boards + WHERE machine_id = :machine_id AND may_be_allocated + ORDER BY power_off_timestamp ASC + LIMIT 1 + """; /** * Tell a job that it is allocated. Doesn't set the state. @@ -688,25 +797,30 @@ public abstract class SQLQueries { @Parameter("num_boards") @Parameter("allocated_board_id") @Parameter("job_id") - protected static final String ALLOCATE_BOARDS_JOB = "UPDATE jobs SET " - + "width = :width, height = :height, depth = :depth, " - + "num_pending = 0, root_id = :board_id, " - + "allocation_size = :num_boards, " - + "allocation_timestamp = UNIX_TIMESTAMP(), " - + "allocated_root = :allocated_board_id " - + "WHERE job_id = :job_id"; - - /** Get a board's ID by its coordinates. */ + protected static final String ALLOCATE_BOARDS_JOB = """ + UPDATE jobs + SET width = :width, height = :height, depth = :depth, + num_pending = 0, root_id = :board_id, + allocation_size = :num_boards, + allocation_timestamp = UNIX_TIMESTAMP(), + allocated_root = :allocated_board_id + WHERE job_id = :job_id + """; + + /** Get an available board's ID by its coordinates. */ @Parameter("machine_id") @Parameter("x") @Parameter("y") @Parameter("z") @ResultColumn("board_id") @SingleRowResult - protected static final String GET_BOARD_BY_COORDS = - "SELECT board_id FROM boards WHERE machine_id = :machine_id " - + "AND x = :x AND y = :y AND z = :z " - + "AND may_be_allocated LIMIT 1"; + protected static final String GET_BOARD_BY_COORDS = """ + SELECT board_id + FROM boards + WHERE machine_id = :machine_id AND x = :x AND y = :y AND z = :z + AND may_be_allocated + LIMIT 1 + """; /** * Tell a board that it is allocated. @@ -715,9 +829,11 @@ public abstract class SQLQueries { */ @Parameter("job_id") @Parameter("board_id") - protected static final String ALLOCATE_BOARDS_BOARD = - "UPDATE boards SET allocated_job = :job_id " - + "WHERE board_id = :board_id"; + protected static final String ALLOCATE_BOARDS_BOARD = """ + UPDATE boards + SET allocated_job = :job_id + WHERE board_id = :board_id + """; /** * Tell the boards of a job that they're no longer allocated to the job. @@ -726,27 +842,34 @@ public abstract class SQLQueries { */ @Parameter("job_id") @Parameter("bmp_id") - protected static final String DEALLOCATE_BMP_BOARDS_JOB = - "UPDATE boards SET allocated_job = NULL " - + "WHERE allocated_job = :job_id AND bmp_id = :bmp_id"; + protected static final String DEALLOCATE_BMP_BOARDS_JOB = """ + UPDATE boards + SET allocated_job = NULL + WHERE allocated_job = :job_id + AND bmp_id = :bmp_id + """; /** - * Set the power state of a board. + * Set the power state of a board to ON. */ @Parameter("board_id") - protected static final String SET_BOARD_POWER_ON = - "UPDATE boards SET board_power = 1, " - + "power_on_timestamp = UNIX_TIMESTAMP() " - + "WHERE board_id = :board_id"; + protected static final String SET_BOARD_POWER_ON = """ + UPDATE boards + SET board_power = 1, + power_on_timestamp = UNIX_TIMESTAMP() + WHERE board_id = :board_id + """; /** - * Set the power state of a board. + * Set the power state of a board to OFF. */ @Parameter("board_id") - protected static final String SET_BOARD_POWER_OFF = - "UPDATE boards SET board_power = 0, " - + "power_off_timestamp = UNIX_TIMESTAMP() " - + "WHERE board_id = :board_id"; + protected static final String SET_BOARD_POWER_OFF = """ + UPDATE boards + SET board_power = 0, + power_off_timestamp = UNIX_TIMESTAMP() + WHERE board_id = :board_id + """; /** * Find jobs that have expired their keepalive interval. @@ -754,11 +877,11 @@ public abstract class SQLQueries { * @see AllocatorTask */ @ResultColumn("job_id") - protected static final String FIND_EXPIRED_JOBS = // - "SELECT job_id FROM jobs " // - + "WHERE job_state != 4 " // DESTROYED - + "AND keepalive_timestamp + keepalive_interval < " - + "UNIX_TIMESTAMP()"; + protected static final String FIND_EXPIRED_JOBS = """ + SELECT job_id FROM jobs + WHERE job_state != 4 -- job is not DESTROYED + AND keepalive_timestamp + keepalive_interval < UNIX_TIMESTAMP() + """; /** * Set the state and number of pending changes for a job. @@ -768,9 +891,11 @@ public abstract class SQLQueries { @Parameter("job_state") @Parameter("num_pending") @Parameter("job_id") - protected static final String SET_STATE_PENDING = - "UPDATE jobs SET job_state = :job_state, " - + "num_pending = :num_pending WHERE job_id = :job_id"; + protected static final String SET_STATE_PENDING = """ + UPDATE jobs + SET job_state = :job_state, num_pending = :num_pending + WHERE job_id = :job_id + """; /** * Set the state destroyed and number of pending changes for a job. @@ -779,24 +904,30 @@ public abstract class SQLQueries { */ @Parameter("num_pending") @Parameter("job_id") - protected static final String SET_STATE_DESTROYED = - "UPDATE jobs SET job_state = 4, " - + "num_pending = :num_pending, " - + "death_timestamp = UNIX_TIMESTAMP() " - + "WHERE job_id = :job_id"; + protected static final String SET_STATE_DESTROYED = """ + UPDATE jobs + SET job_state = 4, + num_pending = :num_pending, + death_timestamp = UNIX_TIMESTAMP() + WHERE job_id = :job_id + """; /** Delete a request to allocate resources for a job. */ @Parameter("job_id") - protected static final String KILL_JOB_ALLOC_TASK = - "DELETE FROM job_request WHERE job_id = :job_id"; + protected static final String KILL_JOB_ALLOC_TASK = """ + DELETE FROM job_request + WHERE job_id = :job_id + """; /** * Delete a request to change the power of a board. Used once the change has * been completed. */ @Parameter("change_id") - protected static final String FINISHED_PENDING = - "DELETE FROM pending_changes WHERE change_id = :change_id"; + protected static final String FINISHED_PENDING = """ + DELETE FROM pending_changes + WHERE change_id = :change_id + """; /** * Get descriptions of how to move from a board to its neighbours. @@ -808,8 +939,10 @@ public abstract class SQLQueries { @ResultColumn("dx") @ResultColumn("dy") @ResultColumn("dz") - protected static final String LOAD_DIR_INFO = - "SELECT z, direction, dx, dy, dz FROM movement_directions"; + protected static final String LOAD_DIR_INFO = """ + SELECT z, direction, dx, dy, dz + FROM movement_directions + """; /** * Get the requests to change the power of boards on a BMP. @@ -829,13 +962,15 @@ public abstract class SQLQueries { @ResultColumn("to_state") @ResultColumn("board_num") @ResultColumn("bmp_id") - protected static final String GET_CHANGES = - "SELECT change_id, job_id, pending_changes.board_id, power, " - + "fpga_n, fpga_s, fpga_e, fpga_w, fpga_se, fpga_nw, " - + "from_state, to_state, board_num, bmp_id " - + "FROM pending_changes JOIN boards USING (board_id) " - + "WHERE bmp_id = :bmp_id " - + "ORDER BY change_id"; + protected static final String GET_CHANGES = """ + SELECT change_id, job_id, pending_changes.board_id, power, + fpga_n, fpga_s, fpga_e, fpga_w, fpga_se, fpga_nw, + from_state, to_state, board_num, bmp_id + FROM pending_changes + JOIN boards USING (board_id) + WHERE bmp_id = :bmp_id + ORDER BY change_id + """; /** * Get the number of changes that are not completed for a job. @@ -844,10 +979,13 @@ public abstract class SQLQueries { @Parameter("from_state") @Parameter("to_state") @ResultColumn("n_changes") - protected static final String COUNT_CHANGES_FOR_JOB = - "SELECT COUNT(change_id) as n_changes from pending_changes " - + "WHERE job_id = :job_id AND from_state = :from_state " - + "AND to_state = :to_state"; + protected static final String COUNT_CHANGES_FOR_JOB = """ + SELECT COUNT(change_id) AS n_changes + FROM pending_changes + WHERE job_id = :job_id + AND from_state = :from_state + AND to_state = :to_state + """; /** * Insert a BMP. @@ -859,9 +997,11 @@ public abstract class SQLQueries { @Parameter("cabinet") @Parameter("frame") @GeneratesID - protected static final String INSERT_BMP = - "INSERT INTO bmp(machine_id, address, cabinet, frame) " - + "VALUES(:machine_id, :address, :cabinet, :frame)"; + protected static final String INSERT_BMP = """ + INSERT INTO bmp( + machine_id, address, cabinet, frame) + VALUES(:machine_id, :address, :cabinet, :frame) + """; /** * Insert a board. @@ -879,11 +1019,14 @@ public abstract class SQLQueries { @Parameter("root_y") @Parameter("enabled") @GeneratesID - protected static final String INSERT_BOARD = "INSERT INTO boards(" - + "machine_id, address, bmp_id, board_num, x, y, z, " - + "root_x, root_y, functioning) VALUES(" - + ":machine_id, :address, :bmp_id, :board_num, :x, :y, :z, " - + ":root_x, :root_y, :enabled)"; + protected static final String INSERT_BOARD = """ + INSERT INTO boards( + machine_id, address, bmp_id, board_num, x, y, z, + root_x, root_y, functioning) + VALUES( + :machine_id, :address, :bmp_id, :board_num, :x, :y, :z, + :root_x, :root_y, :enabled) + """; /** * Insert a link. @@ -896,9 +1039,11 @@ public abstract class SQLQueries { @Parameter("dir_2") @Parameter("live") @GeneratesID - protected static final String INSERT_LINK = - "INSERT IGNORE INTO links(board_1, dir_1, board_2, dir_2, live) " - + "VALUES (:board_1, :dir_1, :board_2, :dir_2, :live)"; + protected static final String INSERT_LINK = """ + INSERT IGNORE INTO links( + board_1, dir_1, board_2, dir_2, live) + VALUES (:board_1, :dir_1, :board_2, :dir_2, :live) + """; /** * Insert a machine. @@ -910,10 +1055,11 @@ public abstract class SQLQueries { @Parameter("height") @Parameter("depth") @GeneratesID - protected static final String INSERT_MACHINE_SPINN_5 = - "INSERT INTO machines(machine_name, " - + "width, height, depth, board_model) " - + "VALUES(:name, :width, :height, :depth, 5)"; + protected static final String INSERT_MACHINE_SPINN_5 = """ + INSERT INTO machines( + machine_name, width, height, depth, board_model) + VALUES(:name, :width, :height, :depth, 5) + """; /** * Insert a tag. @@ -924,8 +1070,11 @@ public abstract class SQLQueries { @Parameter("machine_id") @Parameter("tag") @GeneratesID - protected static final String INSERT_TAG = - "INSERT INTO tags(machine_id, tag) VALUES(:machine_id, :tag)"; + protected static final String INSERT_TAG = """ + INSERT INTO tags( + machine_id, tag) + VALUES(:machine_id, :tag) + """; /** * Delete all tags for a machine. @@ -933,8 +1082,10 @@ public abstract class SQLQueries { * @see MachineStateControl */ @Parameter("machine_id") - protected static final String DELETE_MACHINE_TAGS = - "DELETE FROM tags WHERE machine_id = :machine_id"; + protected static final String DELETE_MACHINE_TAGS = """ + DELETE FROM tags + WHERE machine_id = :machine_id + """; /** * Set the in-service flag for a machine. @@ -943,9 +1094,11 @@ public abstract class SQLQueries { */ @Parameter("in_service") @Parameter("machine_name") - protected static final String SET_MACHINE_STATE = - "UPDATE machines SET in_service = :in_service " - + "WHERE machine_name = :machine_name"; + protected static final String SET_MACHINE_STATE = """ + UPDATE machines + SET in_service = :in_service + WHERE machine_name = :machine_name + """; /** * Note down the maximum chip coordinates so we can calculate wraparounds @@ -956,9 +1109,11 @@ public abstract class SQLQueries { @Parameter("max_x") @Parameter("max_y") @Parameter("machine_id") - protected static final String SET_MAX_COORDS = - "UPDATE machines SET max_chip_x = :max_x, max_chip_y = :max_y " - + "WHERE machine_id = :machine_id"; + protected static final String SET_MAX_COORDS = """ + UPDATE machines + SET max_chip_x = :max_x, max_chip_y = :max_y + WHERE machine_id = :machine_id + """; /** Get a board's full coordinate info given it's ID. */ @Parameter("board_id") @@ -976,15 +1131,18 @@ public abstract class SQLQueries { @ResultColumn("bmp_id") @ResultColumn("machine_id") @SingleRowResult - protected static final String FIND_BOARD_BY_ID = - "SELECT boards.board_id, boards.x, boards.y, boards.z, " - + "bmp.cabinet, bmp.frame, board_num, boards.address, " - + "machines.machine_name, bmp_serial_id, " - + "physical_serial_id, bmp_id, boards.machine_id " - + "FROM boards JOIN machines USING (machine_id) " - + "JOIN bmp USING (bmp_id) " - + "LEFT JOIN board_serial USING (board_id) " - + "WHERE boards.board_id = :board_id LIMIT 1"; + protected static final String FIND_BOARD_BY_ID = """ + SELECT boards.board_id, boards.x, boards.y, boards.z, + bmp.cabinet, bmp.frame, board_num, boards.address, + machines.machine_name, bmp_serial_id, + physical_serial_id, bmp_id, boards.machine_id + FROM boards + JOIN machines USING (machine_id) + JOIN bmp USING (bmp_id) + LEFT JOIN board_serial USING (board_id) + WHERE boards.board_id = :board_id + LIMIT 1 + """; /** Get a board's ID given it's triad coordinates. */ @Parameter("machine_name") @@ -1005,16 +1163,19 @@ public abstract class SQLQueries { @ResultColumn("bmp_id") @ResultColumn("machine_id") @SingleRowResult - protected static final String FIND_BOARD_BY_NAME_AND_XYZ = - "SELECT boards.board_id, boards.x, boards.y, boards.z, " - + "bmp.cabinet, bmp.frame, board_num, boards.address, " - + "machines.machine_name, bmp_serial_id, " - + "physical_serial_id, bmp_id, boards.machine_id " - + "FROM boards JOIN machines USING (machine_id) " - + "JOIN bmp USING (bmp_id) " - + "LEFT JOIN board_serial USING (board_id) " - + "WHERE machine_name = :machine_name " - + "AND x = :x AND y = :y AND z = :z LIMIT 1"; + protected static final String FIND_BOARD_BY_NAME_AND_XYZ = """ + SELECT boards.board_id, boards.x, boards.y, boards.z, + bmp.cabinet, bmp.frame, board_num, boards.address, + machines.machine_name, bmp_serial_id, + physical_serial_id, bmp_id, boards.machine_id + FROM boards + JOIN machines USING (machine_id) + JOIN bmp USING (bmp_id) + LEFT JOIN board_serial USING (board_id) + WHERE machine_name = :machine_name + AND x = :x AND y = :y AND z = :z + LIMIT 1 + """; /** Get a board's ID given it's physical coordinates. */ @Parameter("machine_name") @@ -1035,18 +1196,21 @@ public abstract class SQLQueries { @ResultColumn("bmp_id") @ResultColumn("machine_id") @SingleRowResult - protected static final String FIND_BOARD_BY_NAME_AND_CFB = - "SELECT boards.board_id, boards.x, boards.y, boards.z, " - + "bmp.cabinet, bmp.frame, board_num, boards.address, " - + "machines.machine_name, bmp_serial_id, " - + "physical_serial_id, bmp_id, boards.machine_id " - + "FROM boards JOIN machines USING (machine_id) " - + "JOIN bmp USING (bmp_id) " - + "LEFT JOIN board_serial USING (board_id) " - + "WHERE machine_name = :machine_name " - + "AND bmp.cabinet = :cabinet AND bmp.frame = :frame " - + "AND boards.board_num IS NOT NULL " - + "AND boards.board_num = :board LIMIT 1"; + protected static final String FIND_BOARD_BY_NAME_AND_CFB = """ + SELECT boards.board_id, boards.x, boards.y, boards.z, + bmp.cabinet, bmp.frame, board_num, boards.address, + machines.machine_name, bmp_serial_id, + physical_serial_id, bmp_id, boards.machine_id + FROM boards + JOIN machines USING (machine_id) + JOIN bmp USING (bmp_id) + LEFT JOIN board_serial USING (board_id) + WHERE machine_name = :machine_name + AND bmp.cabinet = :cabinet AND bmp.frame = :frame + AND boards.board_num IS NOT NULL + AND boards.board_num = :board + LIMIT 1 + """; /** Get a board's ID given it's IP address. */ @Parameter("machine_name") @@ -1065,17 +1229,20 @@ public abstract class SQLQueries { @ResultColumn("bmp_id") @ResultColumn("machine_id") @SingleRowResult - protected static final String FIND_BOARD_BY_NAME_AND_IP_ADDRESS = - "SELECT boards.board_id, boards.x, boards.y, boards.z, " - + "bmp.cabinet, bmp.frame, board_num, boards.address, " - + "machines.machine_name, bmp_serial_id, " - + "physical_serial_id, bmp_id, boards.machine_id " - + "FROM boards JOIN machines USING (machine_id) " - + "JOIN bmp USING (bmp_id) " - + "LEFT JOIN board_serial USING (board_id) " - + "WHERE machine_name = :machine_name " - + "AND boards.address IS NOT NULL " - + "AND boards.address = :address LIMIT 1"; + protected static final String FIND_BOARD_BY_NAME_AND_IP_ADDRESS = """ + SELECT boards.board_id, boards.x, boards.y, boards.z, + bmp.cabinet, bmp.frame, board_num, boards.address, + machines.machine_name, bmp_serial_id, + physical_serial_id, bmp_id, boards.machine_id + FROM boards + JOIN machines USING (machine_id) + JOIN bmp USING (bmp_id) + LEFT JOIN board_serial USING (board_id) + WHERE machine_name = :machine_name + AND boards.address IS NOT NULL + AND boards.address = :address + LIMIT 1 + """; /** * Get the value of a board's {@code functioning} column. @@ -1085,9 +1252,12 @@ public abstract class SQLQueries { @Parameter("board_id") @ResultColumn("functioning") @SingleRowResult - protected static final String GET_FUNCTIONING_FIELD = - "SELECT functioning FROM boards " - + "WHERE board_id = :board_id LIMIT 1"; + protected static final String GET_FUNCTIONING_FIELD = """ + SELECT functioning + FROM boards + WHERE board_id = :board_id + LIMIT 1 + """; /** * Set the value of a board's {@code functioning} column. Enables or @@ -1097,9 +1267,11 @@ public abstract class SQLQueries { */ @Parameter("enabled") @Parameter("board_id") - protected static final String SET_FUNCTIONING_FIELD = - "UPDATE boards SET functioning = :enabled " - + "WHERE board_id = :board_id"; + protected static final String SET_FUNCTIONING_FIELD = """ + UPDATE boards + SET functioning = :enabled + WHERE board_id = :board_id + """; /** * Get the quota for a user. @@ -1110,10 +1282,14 @@ public abstract class SQLQueries { @ResultColumn("quota_total") @ResultColumn("user_id") @SingleRowResult - protected static final String GET_USER_QUOTA = - "SELECT SUM(quota) AS quota_total, quotas.user_id FROM quotas " - + "JOIN user_info USING (user_id) " - + "WHERE user_info.user_name = :user_name AND user_id IS NOT NULL"; + protected static final String GET_USER_QUOTA = """ + SELECT + SUM(quota) AS quota_total, + quotas.user_id + FROM quotas + JOIN user_info USING (user_id) + WHERE user_info.user_name = :user_name AND user_id IS NOT NULL + """; /** * Get the quota for a group. @@ -1123,8 +1299,11 @@ public abstract class SQLQueries { @Parameter("group_id") @ResultColumn("quota") @SingleRowResult - protected static final String GET_GROUP_QUOTA = - "SELECT quota FROM user_groups WHERE group_id = :group_id LIMIT 1"; + protected static final String GET_GROUP_QUOTA = """ + SELECT quota FROM user_groups + WHERE group_id = :group_id + LIMIT 1 + """; /** * Get the current non-consolidated usage for a group. @@ -1134,9 +1313,11 @@ public abstract class SQLQueries { @Parameter("group_id") @ResultColumn("current_usage") @SingleRowResult - protected static final String GET_CURRENT_USAGE = - "SELECT COALESCE(SUM(quota_used), 0) AS current_usage" - + " FROM jobs_usage WHERE group_id = :group_id"; + protected static final String GET_CURRENT_USAGE = """ + SELECT COALESCE(SUM(quota_used), 0) AS current_usage + FROM jobs_usage + WHERE group_id = :group_id + """; /** * Get usage of a job and the quota against which that applies. @@ -1147,10 +1328,13 @@ public abstract class SQLQueries { @ResultColumn("quota_used") @ResultColumn("quota") @SingleRowResult - protected static final String GET_JOB_USAGE_AND_QUOTA = - "SELECT quota_used, quota FROM jobs_usage " - + "WHERE job_id = :job_id AND quota_used IS NOT NULL " - + "AND quota IS NOT NULL LIMIT 1"; + protected static final String GET_JOB_USAGE_AND_QUOTA = """ + SELECT quota_used, quota FROM jobs_usage + WHERE job_id = :job_id + AND quota_used IS NOT NULL + AND quota IS NOT NULL + LIMIT 1 + """; /** * Get resource usage info about completed jobs that have yet to be @@ -1161,9 +1345,11 @@ public abstract class SQLQueries { @ResultColumn("job_id") @ResultColumn("group_id") @ResultColumn("quota_used") - protected static final String GET_CONSOLIDATION_TARGETS = - "SELECT job_id, group_id, quota_used FROM jobs_usage " - + "WHERE complete AND quota IS NOT NULL"; + protected static final String GET_CONSOLIDATION_TARGETS = """ + SELECT job_id, group_id, quota_used + FROM jobs_usage + WHERE complete AND quota IS NOT NULL + """; /** * Reduce a group's quota on a machine by a specified amount. @@ -1172,9 +1358,11 @@ public abstract class SQLQueries { */ @Parameter("usage") @Parameter("group_id") - protected static final String DECREMENT_QUOTA = - "UPDATE user_groups SET quota = quota - :usage " - + "WHERE group_id = :group_id AND quota IS NOT NULL"; + protected static final String DECREMENT_QUOTA = """ + UPDATE user_groups + SET quota = quota - :usage + WHERE group_id = :group_id AND quota IS NOT NULL + """; /** * Mark a job as having had its resource usage consolidated. @@ -1182,8 +1370,11 @@ public abstract class SQLQueries { * @see QuotaManager */ @Parameter("job_id") - protected static final String MARK_CONSOLIDATED = - "UPDATE jobs SET accounted_for = 1 WHERE job_id = :job_id"; + protected static final String MARK_CONSOLIDATED = """ + UPDATE jobs + SET accounted_for = 1 + WHERE job_id = :job_id + """; /** * Add or remove time from a quota. Used when someone's administratively @@ -1193,9 +1384,11 @@ public abstract class SQLQueries { */ @Parameter("delta") @Parameter("group_id") - protected static final String ADJUST_QUOTA = - "UPDATE user_groups SET quota = GREATEST(0, quota + :delta) " - + "WHERE group_id = :group_id AND quota IS NOT NULL"; + protected static final String ADJUST_QUOTA = """ + UPDATE user_groups + SET quota = GREATEST(0, quota + :delta) + WHERE group_id = :group_id AND quota IS NOT NULL + """; /** * Set a quota. Used when the system is aligning quota with NMPI data. @@ -1204,9 +1397,12 @@ public abstract class SQLQueries { */ @Parameter("new_quota") @Parameter("group_name") - protected static final String SET_COLLAB_QUOTA = - "UPDATE user_groups SET quota = GREATEST(0, :new_quota) " - + "WHERE group_name = :group_name AND quota IS NOT NULL"; + protected static final String SET_COLLAB_QUOTA = """ + UPDATE user_groups + SET quota = GREATEST(0, :new_quota) + WHERE group_name = :group_name + AND quota IS NOT NULL + """; /** * Get details about a user. This is pretty much everything except their @@ -1226,13 +1422,16 @@ public abstract class SQLQueries { @ResultColumn("openid_subject") @ResultColumn("is_internal") @SingleRowResult - protected static final String GET_USER_DETAILS = - "SELECT user_id, user_name, " - + "encrypted_password IS NOT NULL AS has_password, " - + "trust_level, locked, disabled, " - + "last_successful_login_timestamp, " - + "last_fail_timestamp, openid_subject, is_internal " - + "FROM user_info WHERE user_id = :user_id LIMIT 1"; + protected static final String GET_USER_DETAILS = """ + SELECT user_id, user_name, + encrypted_password IS NOT NULL AS has_password, + trust_level, locked, disabled, + last_successful_login_timestamp, + last_fail_timestamp, openid_subject, is_internal + FROM user_info + WHERE user_id = :user_id + LIMIT 1 + """; /** * Get details about a user. This is pretty much everything except their @@ -1252,13 +1451,16 @@ public abstract class SQLQueries { @ResultColumn("openid_subject") @ResultColumn("is_internal") @SingleRowResult - protected static final String GET_USER_DETAILS_BY_NAME = - "SELECT user_id, user_name, " - + "encrypted_password IS NOT NULL AS has_password, " - + "trust_level, locked, disabled, " - + "last_successful_login_timestamp, " - + "last_fail_timestamp, openid_subject, is_internal " - + "FROM user_info WHERE user_name = :user_name LIMIT 1"; + protected static final String GET_USER_DETAILS_BY_NAME = """ + SELECT user_id, user_name, + encrypted_password IS NOT NULL AS has_password, + trust_level, locked, disabled, + last_successful_login_timestamp, + last_fail_timestamp, openid_subject, is_internal + FROM user_info + WHERE user_name = :user_name + LIMIT 1 + """; /** * Get details about a user. This is pretty much everything except their @@ -1278,14 +1480,16 @@ public abstract class SQLQueries { @ResultColumn("openid_subject") @ResultColumn("is_internal") @SingleRowResult - protected static final String GET_USER_DETAILS_BY_SUBJECT = - "SELECT user_id, user_name, " - + "encrypted_password IS NOT NULL AS has_password, " - + "trust_level, locked, disabled, " - + "last_successful_login_timestamp, " - + "last_fail_timestamp, openid_subject, is_internal " - + "FROM user_info WHERE openid_subject = :openid_subject " - + "LIMIT 1"; + protected static final String GET_USER_DETAILS_BY_SUBJECT = """ + SELECT user_id, user_name, + encrypted_password IS NOT NULL AS has_password, + trust_level, locked, disabled, + last_successful_login_timestamp, + last_fail_timestamp, openid_subject, is_internal + FROM user_info + WHERE openid_subject = :openid_subject + LIMIT 1 + """; /** * Get a local user's basic details. @@ -1297,9 +1501,12 @@ public abstract class SQLQueries { @ResultColumn("user_name") @ResultColumn("encrypted_password") @SingleRowResult - protected static final String GET_LOCAL_USER_DETAILS = - "SELECT user_id, user_name, encrypted_password FROM user_info " - + "WHERE user_name = :user_name AND is_internal LIMIT 1"; + protected static final String GET_LOCAL_USER_DETAILS = """ + SELECT user_id, user_name, encrypted_password + FROM user_info + WHERE user_name = :user_name AND is_internal + LIMIT 1 + """; /** * Get the ID of a particular group that a particular user must be a member @@ -1311,12 +1518,15 @@ public abstract class SQLQueries { @Parameter("group_name") @ResultColumn("group_id") @SingleRowResult - protected static final String GET_GROUP_BY_NAME_AND_MEMBER = - "SELECT user_groups.group_id FROM user_groups " - + "JOIN group_memberships USING (group_id) " - + "JOIN user_info USING (user_id) " - + "WHERE user_name = :user_name " - + "AND group_name = :group_name LIMIT 1"; + protected static final String GET_GROUP_BY_NAME_AND_MEMBER = """ + SELECT user_groups.group_id + FROM user_groups + JOIN group_memberships USING (group_id) + JOIN user_info USING (user_id) + WHERE user_name = :user_name + AND group_name = :group_name + LIMIT 1 + """; /** * List all the groups that a user is a member of and that have quota left. @@ -1325,12 +1535,13 @@ public abstract class SQLQueries { */ @Parameter("user_name") @ResultColumn("group_name") - protected static final String GET_GROUP_NAMES_OF_USER = - "SELECT user_groups.group_name " - + "FROM group_memberships " - + "JOIN user_info USING (user_id) " - + "JOIN user_groups USING (group_id) " - + "WHERE user_name = :user_name"; + protected static final String GET_GROUP_NAMES_OF_USER = """ + SELECT user_groups.group_name + FROM group_memberships + JOIN user_info USING (user_id) + JOIN user_groups USING (group_id) + WHERE user_name = :user_name + """; /** * List the members of a group. @@ -1343,13 +1554,15 @@ public abstract class SQLQueries { @ResultColumn("group_name") @ResultColumn("user_id") @ResultColumn("user_name") - protected static final String GET_USERS_OF_GROUP = - "SELECT membership_id, user_groups.group_id, " - + "user_groups.group_name, " - + "user_info.user_id, user_info.user_name " - + "FROM group_memberships JOIN user_info USING (user_id) " - + "JOIN user_groups USING (group_id) " - + "WHERE group_id = :group_id"; + protected static final String GET_USERS_OF_GROUP = """ + SELECT membership_id, user_groups.group_id, + user_groups.group_name, + user_info.user_id, user_info.user_name + FROM group_memberships + JOIN user_info USING (user_id) + JOIN user_groups USING (group_id) + WHERE group_id = :group_id + """; /** * Get the details from a specific membership. @@ -1363,12 +1576,14 @@ public abstract class SQLQueries { @ResultColumn("user_name") @ResultColumn("group_name") @SingleRowResult - protected static final String GET_MEMBERSHIP = - "SELECT membership_id, user_info.user_id, user_groups.group_id, " - + "user_name, group_name FROM group_memberships " - + "JOIN user_info USING (user_id) " - + "JOIN user_groups USING (group_id) " - + "WHERE membership_id = :membership_id"; + protected static final String GET_MEMBERSHIP = """ + SELECT membership_id, user_info.user_id, user_groups.group_id, + user_name, group_name + FROM group_memberships + JOIN user_info USING (user_id) + JOIN user_groups USING (group_id) + WHERE membership_id = :membership_id + """; /** * Get the memberships of a user. @@ -1381,12 +1596,14 @@ public abstract class SQLQueries { @ResultColumn("group_id") @ResultColumn("group_name") @ResultColumn("user_name") - protected static final String GET_MEMBERSHIPS_OF_USER = - "SELECT membership_id, user_info.user_id, user_groups.group_id, " - + "user_name, group_name FROM group_memberships " - + "JOIN user_info USING (user_id) " - + "JOIN user_groups USING (group_id) " - + "WHERE group_memberships.user_id = :user_id"; + protected static final String GET_MEMBERSHIPS_OF_USER = """ + SELECT membership_id, user_info.user_id, user_groups.group_id, + user_name, group_name + FROM group_memberships + JOIN user_info USING (user_id) + JOIN user_groups USING (group_id) + WHERE group_memberships.user_id = :user_id + """; /** * Create a group record. @@ -1397,9 +1614,11 @@ public abstract class SQLQueries { @Parameter("quota") @Parameter("group_type") @GeneratesID - protected static final String CREATE_GROUP = - "INSERT INTO user_groups(group_name, quota, group_type) " - + "VALUES(:group_name, :quota, :group_type)"; + protected static final String CREATE_GROUP = """ + INSERT INTO user_groups( + group_name, quota, group_type) + VALUES(:group_name, :quota, :group_type) + """; /** * Create a group record. @@ -1409,9 +1628,11 @@ public abstract class SQLQueries { @Parameter("group_name") @Parameter("quota") @Parameter("group_type") - protected static final String CREATE_GROUP_IF_NOT_EXISTS = - "INSERT IGNORE INTO user_groups(group_name, quota, group_type) " - + "VALUES(:group_name, :quota, :group_type)"; + protected static final String CREATE_GROUP_IF_NOT_EXISTS = """ + INSERT IGNORE INTO user_groups( + group_name, quota, group_type) + VALUES(:group_name, :quota, :group_type) + """; /** * Delete a single group record and returns the name of the deleted group. @@ -1421,8 +1642,10 @@ public abstract class SQLQueries { @Parameter("group_id") @ResultColumn("group_name") @SingleRowResult - protected static final String DELETE_GROUP = - "DELETE FROM user_groups WHERE group_id = :group_id "; + protected static final String DELETE_GROUP = """ + DELETE FROM user_groups + WHERE group_id = :group_id + """; /** * Update a single group record's name and quota. @@ -1433,9 +1656,12 @@ public abstract class SQLQueries { @Parameter("quota") @Parameter("group_id") @SingleRowResult - protected static final String UPDATE_GROUP = "UPDATE user_groups SET " - + "group_name = COALESCE(:group_name, group_name), " - + "quota = :quota WHERE group_id = :group_id "; + protected static final String UPDATE_GROUP = """ + UPDATE user_groups + SET group_name = COALESCE(:group_name, group_name), + quota = :quota + WHERE group_id = :group_id + """; /** * Adds a user to a group. @@ -1445,10 +1671,12 @@ public abstract class SQLQueries { @Parameter("user_id") @Parameter("group_id") @GeneratesID - protected static final String ADD_USER_TO_GROUP = - "INSERT INTO group_memberships(user_id, group_id) " - + "VALUES (:user_id, :group_id) " - + "ON DUPLICATE KEY UPDATE user_id=user_id;"; + protected static final String ADD_USER_TO_GROUP = """ + INSERT INTO group_memberships( + user_id, group_id) + VALUES (:user_id, :group_id) + ON DUPLICATE KEY UPDATE user_id = user_id + """; /** * Removes a user from a group. @@ -1457,9 +1685,10 @@ public abstract class SQLQueries { */ @Parameter("user_id") @Parameter("group_id") - protected static final String REMOVE_USER_FROM_GROUP = - "DELETE FROM group_memberships " - + "WHERE user_id = :user_id AND group_id = :group_id"; + protected static final String REMOVE_USER_FROM_GROUP = """ + DELETE FROM group_memberships + WHERE user_id = :user_id AND group_id = :group_id + """; /** * Step 1 of group synchronisation: make a temporary table. Should be @@ -1467,8 +1696,10 @@ public abstract class SQLQueries { * * @see LocalAuthProviderImpl */ - protected static final String GROUP_SYNC_MAKE_TEMP_TABLE = - "CREATE TEMPORARY TABLE usergroupids(group_id INTEGER)"; + protected static final String GROUP_SYNC_MAKE_TEMP_TABLE = """ + CREATE TEMPORARY TABLE usergroupids( + group_id INTEGER) + """; /** * Step 2 of group synchronisation: add a desired group to the temp table. @@ -1480,10 +1711,12 @@ public abstract class SQLQueries { */ @Parameter("group_name") @Parameter("group_type") - protected static final String GROUP_SYNC_INSERT_TEMP_ROW = - "INSERT INTO usergroupids SELECT group_id FROM user_groups " - + "WHERE group_name = :group_name " - + "AND group_type = :group_type"; + protected static final String GROUP_SYNC_INSERT_TEMP_ROW = """ + INSERT INTO usergroupids + SELECT group_id FROM user_groups + WHERE group_name = :group_name + AND group_type = :group_type + """; /** * Step 3 of group synchronisation: add real missing groups. Requires the @@ -1495,10 +1728,12 @@ public abstract class SQLQueries { * @see LocalAuthProviderImpl */ @Parameter("user_id") - protected static final String GROUP_SYNC_ADD_GROUPS = - "INSERT IGNORE INTO group_memberships(user_id, group_id) " - + "SELECT :user_id AS user_id, " - + "group_id FROM usergroupids"; + protected static final String GROUP_SYNC_ADD_GROUPS = """ + INSERT IGNORE INTO group_memberships( + user_id, group_id) + SELECT :user_id AS user_id, group_id + FROM usergroupids + """; /** * Step 4 of group synchronisation: remove real groups no longer wanted. @@ -1510,23 +1745,22 @@ public abstract class SQLQueries { * @see LocalAuthProviderImpl */ @Parameter("user_id") - protected static final String GROUP_SYNC_REMOVE_GROUPS = - "DELETE FROM group_memberships " - + "WHERE user_id = :user_id AND group_id NOT IN (" - + "SELECT group_id FROM usergroupids)"; + protected static final String GROUP_SYNC_REMOVE_GROUPS = """ + DELETE FROM group_memberships + WHERE user_id = :user_id AND group_id NOT IN ( + SELECT group_id FROM usergroupids) + """; /** - * Step 5 of group synchronisation: drop the temporary table. Except we - * don't because that doesn't work in a transaction; squelching the contents - * works though (the table itself is dropped automatically when the - * connection is dropped so we don't need to clean it up here). Should be + * Step 5 of group synchronisation: drop the temporary table. Should be * performed in a transaction with the other group synch steps. Requires * that {@link #GROUP_SYNC_MAKE_TEMP_TABLE} was run first. * * @see LocalAuthProviderImpl */ - protected static final String GROUP_SYNC_DROP_TEMP_TABLE = - "DROP TEMPORARY TABLE usergroupids"; + protected static final String GROUP_SYNC_DROP_TEMP_TABLE = """ + DROP TEMPORARY TABLE usergroupids + """; /** * Test if a user account is locked or disabled. @@ -1538,9 +1772,12 @@ public abstract class SQLQueries { @ResultColumn("locked") @ResultColumn("disabled") @SingleRowResult - protected static final String IS_USER_LOCKED = - "SELECT user_id, locked, disabled FROM user_info " - + "WHERE user_name = :username"; + protected static final String IS_USER_LOCKED = """ + SELECT user_id, locked, disabled + FROM user_info + WHERE user_name = :username + LIMIT 1 + """; /** * Get the permissions and encrypted password for a user. @@ -1552,9 +1789,12 @@ public abstract class SQLQueries { @ResultColumn("encrypted_password") @ResultColumn("openid_subject") @SingleRowResult - protected static final String GET_USER_AUTHORITIES = - "SELECT trust_level, encrypted_password, openid_subject " - + "FROM user_info WHERE user_id = :user_id LIMIT 1"; + protected static final String GET_USER_AUTHORITIES = """ + SELECT trust_level, encrypted_password, openid_subject + FROM user_info + WHERE user_id = :user_id + LIMIT 1 + """; /** * Note the login success. @@ -1563,10 +1803,12 @@ public abstract class SQLQueries { */ @Parameter("openid_subject") @Parameter("user_id") - protected static final String MARK_LOGIN_SUCCESS = "UPDATE user_info SET " - + "last_successful_login_timestamp = UNIX_TIMESTAMP(), " - + "failure_count = 0, openid_subject = :openid_subject " - + "WHERE user_id = :user_id"; + protected static final String MARK_LOGIN_SUCCESS = """ + UPDATE user_info + SET last_successful_login_timestamp = UNIX_TIMESTAMP(), + failure_count = 0, openid_subject = :openid_subject + WHERE user_id = :user_id + """; /** * Note the login failure. @@ -1575,11 +1817,13 @@ public abstract class SQLQueries { */ @Parameter("failure_limit") @Parameter("user_id") - protected static final String MARK_LOGIN_FAILURE = - "UPDATE user_info SET failure_count = failure_count + 1, " - + "last_fail_timestamp = UNIX_TIMESTAMP(), " - + "locked = (failure_count + 1 >= :failure_limit) " - + "WHERE user_id = :user_id"; + protected static final String MARK_LOGIN_FAILURE = """ + UPDATE user_info + SET failure_count = failure_count + 1, + last_fail_timestamp = UNIX_TIMESTAMP(), + locked = (failure_count + 1 >= :failure_limit) + WHERE user_id = :user_id + """; /** * Unlock accounts. @@ -1587,11 +1831,12 @@ public abstract class SQLQueries { * @see LocalAuthProviderImpl */ @Parameter("lock_interval") - protected static final String UNLOCK_LOCKED_USERS = - "UPDATE user_info SET failure_count = 0, last_fail_timestamp = 0, " - + "locked = 0 WHERE last_fail_timestamp + :lock_interval " - + "< UNIX_TIMESTAMP() " - + "AND locked"; + protected static final String UNLOCK_LOCKED_USERS = """ + UPDATE user_info + SET failure_count = 0, last_fail_timestamp = 0, locked = 0 + WHERE last_fail_timestamp + :lock_interval < UNIX_TIMESTAMP() + AND locked + """; /** * Delete a user. @@ -1599,8 +1844,10 @@ public abstract class SQLQueries { * @see UserControl */ @Parameter("user_id") - protected static final String DELETE_USER = - "DELETE FROM user_info WHERE user_id = :user_id"; + protected static final String DELETE_USER = """ + DELETE FROM user_info + WHERE user_id = :user_id + """; /** * Get the ID of a user. Used for safety checks. @@ -1611,8 +1858,12 @@ public abstract class SQLQueries { @Parameter("user_name") @ResultColumn("user_id") @SingleRowResult - protected static final String GET_USER_ID = "SELECT user_id FROM user_info " - + "WHERE user_name = :user_name LIMIT 1"; + protected static final String GET_USER_ID = """ + SELECT user_id + FROM user_info + WHERE user_name = :user_name + LIMIT 1 + """; /** * Set the amount a user is trusted. @@ -1621,9 +1872,11 @@ public abstract class SQLQueries { */ @Parameter("trust") @Parameter("user_id") - protected static final String SET_USER_TRUST = - "UPDATE user_info SET trust_level = :trust " - + "WHERE user_id = :user_id"; + protected static final String SET_USER_TRUST = """ + UPDATE user_info + SET trust_level = :trust + WHERE user_id = :user_id + """; /** * Set whether a user is locked. Can be used to unlock a user early. @@ -1632,8 +1885,11 @@ public abstract class SQLQueries { */ @Parameter("locked") @Parameter("user_id") - protected static final String SET_USER_LOCKED = - "UPDATE user_info SET locked = :locked WHERE user_id = :user_id"; + protected static final String SET_USER_LOCKED = """ + UPDATE user_info + SET locked = :locked + WHERE user_id = :user_id + """; /** * Set whether a user is administratively disabled. @@ -1642,9 +1898,11 @@ public abstract class SQLQueries { */ @Parameter("disabled") @Parameter("user_id") - protected static final String SET_USER_DISABLED = - "UPDATE user_info SET disabled = :disabled " - + "WHERE user_id = :user_id"; + protected static final String SET_USER_DISABLED = """ + UPDATE user_info + SET disabled = :disabled + WHERE user_id = :user_id + """; /** * Set a user's password. Passwords are either encrypted (with bcrypt) or @@ -1655,9 +1913,11 @@ public abstract class SQLQueries { */ @Parameter("password") @Parameter("user_id") - protected static final String SET_USER_PASS = - "UPDATE user_info SET encrypted_password = :password " - + "WHERE user_id = :user_id"; + protected static final String SET_USER_PASS = """ + UPDATE user_info + SET encrypted_password = :password + WHERE user_id = :user_id + """; /** * Set a user's name. @@ -1666,9 +1926,11 @@ public abstract class SQLQueries { */ @Parameter("user_name") @Parameter("user_id") - protected static final String SET_USER_NAME = - "UPDATE user_info SET user_name = :user_name " - + "WHERE user_id = :user_id"; + protected static final String SET_USER_NAME = """ + UPDATE user_info + SET user_name = :user_name + WHERE user_id = :user_id + """; /** * Get a list of all users. @@ -1678,8 +1940,10 @@ public abstract class SQLQueries { @ResultColumn("user_id") @ResultColumn("user_name") @ResultColumn("openid_subject") - protected static final String LIST_ALL_USERS = - "SELECT user_id, user_name, openid_subject FROM user_info"; + protected static final String LIST_ALL_USERS = """ + SELECT user_id, user_name, openid_subject + FROM user_info + """; /** * Get a list of all users. @@ -1690,9 +1954,11 @@ public abstract class SQLQueries { @ResultColumn("user_id") @ResultColumn("user_name") @ResultColumn("openid_subject") - protected static final String LIST_ALL_USERS_OF_TYPE = - "SELECT user_id, user_name, openid_subject FROM user_info " - + "WHERE is_internal = :internal"; + protected static final String LIST_ALL_USERS_OF_TYPE = """ + SELECT user_id, user_name, openid_subject + FROM user_info + WHERE is_internal = :internal + """; /** * Create a user. Passwords are either encrypted (with bcrypt) or @@ -1707,11 +1973,13 @@ public abstract class SQLQueries { @Parameter("disabled") @Parameter("openid_subject") @GeneratesID - protected static final String CREATE_USER = - "INSERT IGNORE INTO user_info(user_name, encrypted_password, " - + "trust_level, disabled, openid_subject) " - + "VALUES(:user_name, :encoded_password, :trust_level, " - + ":disabled, :openid_subject)"; + protected static final String CREATE_USER = """ + INSERT IGNORE INTO user_info( + user_name, encrypted_password, trust_level, disabled, + openid_subject) + VALUES(:user_name, :encoded_password, :trust_level, :disabled, + :openid_subject) + """; /** * Get a list of all groups. @@ -1722,8 +1990,10 @@ public abstract class SQLQueries { @ResultColumn("group_name") @ResultColumn("quota") @ResultColumn("group_type") - protected static final String LIST_ALL_GROUPS = - "SELECT group_id, group_name, quota, group_type FROM user_groups"; + protected static final String LIST_ALL_GROUPS = """ + SELECT group_id, group_name, quota, group_type + FROM user_groups + """; /** * Get a list of all groups of a given type. @@ -1735,9 +2005,11 @@ public abstract class SQLQueries { @ResultColumn("group_name") @ResultColumn("quota") @ResultColumn("group_type") - protected static final String LIST_ALL_GROUPS_OF_TYPE = - "SELECT group_id, group_name, quota, group_type FROM user_groups " - + "WHERE group_type = :type"; + protected static final String LIST_ALL_GROUPS_OF_TYPE = """ + SELECT group_id, group_name, quota, group_type + FROM user_groups + WHERE group_type = :type + """; /** * Get a group by it's ID. @@ -1750,9 +2022,12 @@ public abstract class SQLQueries { @ResultColumn("quota") @ResultColumn("group_type") @SingleRowResult - protected static final String GET_GROUP_BY_ID = - "SELECT group_id, group_name, quota, group_type FROM user_groups " - + "WHERE group_id = :group_id LIMIT 1"; + protected static final String GET_GROUP_BY_ID = """ + SELECT group_id, group_name, quota, group_type + FROM user_groups + WHERE group_id = :group_id + LIMIT 1 + """; /** * Get a group by it's name. @@ -1765,9 +2040,12 @@ public abstract class SQLQueries { @ResultColumn("quota") @ResultColumn("group_type") @SingleRowResult - protected static final String GET_GROUP_BY_NAME = - "SELECT group_id, group_name, quota, group_type FROM user_groups " - + "WHERE group_name = :group_name LIMIT 1"; + protected static final String GET_GROUP_BY_NAME = """ + SELECT group_id, group_name, quota, group_type + FROM user_groups + WHERE group_name = :group_name + LIMIT 1 + """; /** * Create a board issue report. @@ -1779,12 +2057,11 @@ public abstract class SQLQueries { @Parameter("issue") @Parameter("user_id") @GeneratesID - protected static final String INSERT_BOARD_REPORT = - "INSERT INTO board_reports(" - + "board_id, job_id, reported_issue, reporter, " - + "report_timestamp) " - + "VALUES(:board_id, :job_id, :issue, :user_id, " - + "UNIX_TIMESTAMP())"; + protected static final String INSERT_BOARD_REPORT = """ + INSERT INTO board_reports( + board_id, job_id, reported_issue, reporter, report_timestamp) + VALUES(:board_id, :job_id, :issue, :user_id, UNIX_TIMESTAMP()) + """; /** * Actually delete a job record. Only called by the data tombstone-r. This @@ -1793,8 +2070,10 @@ public abstract class SQLQueries { * @see AllocatorTask#tombstone() */ @Parameter("job_id") - protected static final String DELETE_JOB_RECORD = - "DELETE FROM jobs WHERE job_id = :job_id"; + protected static final String DELETE_JOB_RECORD = """ + DELETE FROM jobs + WHERE job_id = :job_id + """; /** * Actually delete an allocation record. Only called by the data @@ -1804,8 +2083,10 @@ public abstract class SQLQueries { * @see AllocatorTask#tombstone() */ @Parameter("alloc_id") - protected static final String DELETE_ALLOC_RECORD = - "DELETE FROM old_board_allocations WHERE alloc_id = :alloc_id"; + protected static final String DELETE_ALLOC_RECORD = """ + DELETE FROM old_board_allocations + WHERE alloc_id = :alloc_id + """; /** * Read the blacklisted chips for a board. @@ -1816,10 +2097,12 @@ public abstract class SQLQueries { @ResultColumn("x") @ResultColumn("y") @ResultColumn("notes") - protected static final String GET_BLACKLISTED_CHIPS = - "SELECT chip_x AS x, chip_y AS y, notes FROM blacklisted_chips " - + "JOIN board_model_coords USING (coord_id) " - + "WHERE board_id = :board_id"; + protected static final String GET_BLACKLISTED_CHIPS = """ + SELECT chip_x AS x, chip_y AS y, notes + FROM blacklisted_chips + JOIN board_model_coords USING (coord_id) + WHERE board_id = :board_id + """; /** * Read the blacklisted cores for a board. @@ -1831,11 +2114,12 @@ public abstract class SQLQueries { @ResultColumn("y") @ResultColumn("p") @ResultColumn("notes") - protected static final String GET_BLACKLISTED_CORES = - "SELECT chip_x AS x, chip_y AS y, physical_core AS p, notes " - + "FROM blacklisted_cores " - + "JOIN board_model_coords USING (coord_id) " - + "WHERE board_id = :board_id"; + protected static final String GET_BLACKLISTED_CORES = """ + SELECT chip_x AS x, chip_y AS y, physical_core AS p, notes + FROM blacklisted_cores + JOIN board_model_coords USING (coord_id) + WHERE board_id = :board_id + """; /** * Read the blacklisted links for a board. @@ -1847,11 +2131,12 @@ public abstract class SQLQueries { @ResultColumn("y") @ResultColumn("direction") @ResultColumn("notes") - protected static final String GET_BLACKLISTED_LINKS = - "SELECT chip_x AS x, chip_y AS y, direction, notes " - + "FROM blacklisted_links " - + "JOIN board_model_coords USING (coord_id) " - + "WHERE board_id = :board_id"; + protected static final String GET_BLACKLISTED_LINKS = """ + SELECT chip_x AS x, chip_y AS y, direction, notes + FROM blacklisted_links + JOIN board_model_coords USING (coord_id) + WHERE board_id = :board_id + """; /** * Mark a board as having had its blacklist modified. @@ -1859,9 +2144,11 @@ public abstract class SQLQueries { * @see MachineStateControl */ @Parameter("board_id") - protected static final String MARK_BOARD_BLACKLIST_CHANGED = - "UPDATE boards SET blacklist_set_timestamp = UNIX_TIMESTAMP() " - + "WHERE board_id = :board_id"; + protected static final String MARK_BOARD_BLACKLIST_CHANGED = """ + UPDATE boards + SET blacklist_set_timestamp = UNIX_TIMESTAMP() + WHERE board_id = :board_id + """; /** * Mark a board as having had its blacklist synchronised with the hardware. @@ -1869,9 +2156,11 @@ public abstract class SQLQueries { * @see MachineStateControl */ @Parameter("board_id") - protected static final String MARK_BOARD_BLACKLIST_SYNCHED = - "UPDATE boards SET blacklist_sync_timestamp = UNIX_TIMESTAMP() " - + "WHERE board_id = :board_id"; + protected static final String MARK_BOARD_BLACKLIST_SYNCHED = """ + UPDATE boards + SET blacklist_sync_timestamp = UNIX_TIMESTAMP() + WHERE board_id = :board_id + """; /** * Asks whether the blacklist model for a board is believed to be @@ -1882,10 +2171,13 @@ public abstract class SQLQueries { @Parameter("board_id") @ResultColumn("current") @SingleRowResult - protected static final String IS_BOARD_BLACKLIST_CURRENT = - "SELECT blacklist_sync_timestamp >= blacklist_set_timestamp " - + "AS current FROM boards WHERE board_id = :board_id " - + "LIMIT 1"; + protected static final String IS_BOARD_BLACKLIST_CURRENT = """ + SELECT blacklist_sync_timestamp >= blacklist_set_timestamp + AS current + FROM boards + WHERE board_id = :board_id + LIMIT 1 + """; /** * Add a chip on a board to that board's blacklist. Does not cause the @@ -1897,15 +2189,18 @@ public abstract class SQLQueries { @Parameter("board_id") @Parameter("x") @Parameter("y") - protected static final String ADD_BLACKLISTED_CHIP = - "INSERT INTO blacklisted_chips(board_id, coord_id, notes) " - + "WITH args(board_id, x, y) AS (SELECT :board_id, :x, :y)," - + "m(model) AS (SELECT board_model FROM machines " - + "JOIN boards USING (machine_id) " - + "JOIN args USING (board_id)) " - + "SELECT args.board_id, coord_id, NULL " - + "FROM board_model_coords JOIN m USING (model) JOIN args " - + "WHERE chip_x = args.x AND chip_y = args.y"; + protected static final String ADD_BLACKLISTED_CHIP = """ + INSERT INTO blacklisted_chips(board_id, coord_id, notes) + WITH args(board_id, x, y) AS (SELECT :board_id, :x, :y), + m(model) AS (SELECT board_model FROM machines + JOIN boards USING (machine_id) + JOIN args USING (board_id)) + SELECT args.board_id, coord_id, NULL + FROM board_model_coords + JOIN m USING (model) + JOIN args + WHERE chip_x = args.x AND chip_y = args.y + """; /** * Add a core on a board to that board's blacklist. Does not cause the @@ -1918,17 +2213,19 @@ public abstract class SQLQueries { @Parameter("x") @Parameter("y") @Parameter("p") - protected static final String ADD_BLACKLISTED_CORE = - "INSERT INTO blacklisted_cores(" - + "board_id, coord_id, physical_core, notes) " - + "WITH args(board_id, x, y, p) AS " - + "(SELECT :board_id, :x, :y, :p), " - + "m(model) AS (SELECT board_model FROM machines " - + "JOIN boards USING (machine_id) " - + "JOIN args USING (board_id)) " - + "SELECT args.board_id, coord_id, p, NULL " - + "FROM board_model_coords JOIN m USING (model) JOIN args " - + "WHERE chip_x = args.x AND chip_y = args.y"; + protected static final String ADD_BLACKLISTED_CORE = """ + INSERT INTO blacklisted_cores( + board_id, coord_id, physical_core, notes) + WITH args(board_id, x, y, p) AS (SELECT :board_id, :x, :y, :p), + m(model) AS (SELECT board_model FROM machines + JOIN boards USING (machine_id) + JOIN args USING (board_id)) + SELECT args.board_id, coord_id, p, NULL + FROM board_model_coords + JOIN m USING (model) + JOIN args + WHERE chip_x = args.x AND chip_y = args.y + """; /** * Add a link on a board to that board's blacklist. Does not cause the @@ -1941,17 +2238,20 @@ public abstract class SQLQueries { @Parameter("x") @Parameter("y") @Parameter("direction") - protected static final String ADD_BLACKLISTED_LINK = - "INSERT INTO blacklisted_links(" - + "board_id, coord_id, direction, notes) " - + "WITH args(board_id, x, y, dir) AS (" - + "SELECT :board_id, :x, :y, :direction), " - + "m(model) AS (SELECT board_model FROM machines " - + "JOIN boards USING (machine_id) " - + "JOIN args USING (board_id)) " - + "SELECT args.board_id, coord_id, dir, NULL " - + "FROM board_model_coords JOIN m USING (model) JOIN args " - + "WHERE chip_x = args.x AND chip_y = args.y"; + protected static final String ADD_BLACKLISTED_LINK = """ + INSERT INTO blacklisted_links( + board_id, coord_id, direction, notes) + WITH args(board_id, x, y, dir) AS ( + SELECT :board_id, :x, :y, :direction), + m(model) AS (SELECT board_model FROM machines + JOIN boards USING (machine_id) + JOIN args USING (board_id)) + SELECT args.board_id, coord_id, dir, NULL + FROM board_model_coords + JOIN m USING (model) + JOIN args + WHERE chip_x = args.x AND chip_y = args.y + """; /** * Delete all blacklist entries for chips on a board. @@ -1959,8 +2259,10 @@ public abstract class SQLQueries { * @see BlacklistStore */ @Parameter("board_id") - protected static final String CLEAR_BLACKLISTED_CHIPS = - "DELETE FROM blacklisted_chips WHERE board_id = :board_id"; + protected static final String CLEAR_BLACKLISTED_CHIPS = """ + DELETE FROM blacklisted_chips + WHERE board_id = :board_id + """; /** * Delete all blacklist entries for cores on a board. @@ -1968,8 +2270,10 @@ public abstract class SQLQueries { * @see BlacklistStore */ @Parameter("board_id") - protected static final String CLEAR_BLACKLISTED_CORES = - "DELETE FROM blacklisted_cores WHERE board_id = :board_id"; + protected static final String CLEAR_BLACKLISTED_CORES = """ + DELETE FROM blacklisted_cores + WHERE board_id = :board_id + """; /** * Delete all blacklist entries for links on a board. @@ -1977,8 +2281,10 @@ public abstract class SQLQueries { * @see BlacklistStore */ @Parameter("board_id") - protected static final String CLEAR_BLACKLISTED_LINKS = - "DELETE FROM blacklisted_links WHERE board_id = :board_id"; + protected static final String CLEAR_BLACKLISTED_LINKS = """ + DELETE FROM blacklisted_links + WHERE board_id = :board_id + """; /** * Get the list of writes (to the machine) of blacklist data to perform. @@ -1993,14 +2299,17 @@ public abstract class SQLQueries { @ResultColumn("cabinet") @ResultColumn("frame") @ResultColumn("machine_id") - protected static final String GET_BLACKLIST_WRITES = - "SELECT op_id, board_id, board_serial.bmp_serial_id, board_num, " - + "cabinet, frame, data, boards.machine_id " - + "FROM blacklist_ops " - + "JOIN boards USING (board_id) JOIN bmp USING (bmp_id) " - + "LEFT JOIN board_serial USING (board_id) " - + "WHERE op = 1 AND NOT completed " - + "AND boards.bmp_id = :bmp_id"; + protected static final String GET_BLACKLIST_WRITES = """ + SELECT op_id, board_id, board_serial.bmp_serial_id, board_num, + cabinet, frame, data, boards.machine_id + FROM blacklist_ops + JOIN boards USING (board_id) + JOIN bmp USING (bmp_id) + LEFT JOIN board_serial USING (board_id) + WHERE op = 1 + AND NOT completed + AND boards.bmp_id = :bmp_id + """; /** * Get the list of reads (from the machine) of blacklist data to perform. @@ -2015,13 +2324,17 @@ public abstract class SQLQueries { @ResultColumn("cabinet") @ResultColumn("frame") @ResultColumn("machine_id") - protected static final String GET_BLACKLIST_READS = - "SELECT op_id, board_id, board_serial.bmp_serial_id, board_num, " - + "cabinet, frame, boards.machine_id FROM blacklist_ops " - + "JOIN boards USING (board_id) JOIN bmp USING (bmp_id) " - + "LEFT JOIN board_serial USING (board_id) " - + "WHERE op = 0 AND NOT completed " - + "AND boards.bmp_id = :bmp_id"; + protected static final String GET_BLACKLIST_READS = """ + SELECT op_id, board_id, board_serial.bmp_serial_id, board_num, + cabinet, frame, boards.machine_id + FROM blacklist_ops + JOIN boards USING (board_id) + JOIN bmp USING (bmp_id) + LEFT JOIN board_serial USING (board_id) + WHERE op = 0 + AND NOT completed + AND boards.bmp_id = :bmp_id + """; /** * Get the list of reads (from the machine) of serial data to perform. @@ -2036,13 +2349,17 @@ public abstract class SQLQueries { @ResultColumn("cabinet") @ResultColumn("frame") @ResultColumn("machine_id") - protected static final String GET_SERIAL_INFO_REQS = - "SELECT op_id, board_id, board_serial.bmp_serial_id, board_num, " - + "cabinet, frame, boards.machine_id FROM blacklist_ops " - + "JOIN boards USING (board_id) JOIN bmp USING (bmp_id) " - + "LEFT JOIN board_serial USING (board_id) " - + "WHERE op = 2 AND NOT completed " - + "AND boards.bmp_id = :bmp_id"; + protected static final String GET_SERIAL_INFO_REQS = """ + SELECT op_id, board_id, board_serial.bmp_serial_id, board_num, + cabinet, frame, boards.machine_id + FROM blacklist_ops + JOIN boards USING (board_id) + JOIN bmp USING (bmp_id) + LEFT JOIN board_serial USING (board_id) + WHERE op = 2 + AND NOT completed + AND boards.bmp_id = :bmp_id + """; /** * Get the list of reads (from the machine) of temperature data to perform. @@ -2057,13 +2374,17 @@ public abstract class SQLQueries { @ResultColumn("cabinet") @ResultColumn("frame") @ResultColumn("machine_id") - protected static final String GET_TEMP_INFO_REQS = - "SELECT op_id, board_id, board_serial.bmp_serial_id, board_num, " - + "cabinet, frame, boards.machine_id FROM blacklist_ops " - + "JOIN boards USING (board_id) JOIN bmp USING (bmp_id) " - + "LEFT JOIN board_serial USING (board_id) " - + "WHERE op = 3 AND NOT completed " - + "AND boards.machine_id = :machine_id"; + protected static final String GET_TEMP_INFO_REQS = """ + SELECT op_id, board_id, board_serial.bmp_serial_id, board_num, + cabinet, frame, boards.machine_id + FROM blacklist_ops + JOIN boards USING (board_id) + JOIN bmp USING (bmp_id) + LEFT JOIN board_serial USING (board_id) + WHERE op = 3 + AND NOT completed + AND boards.machine_id = :machine_id + """; /** * Set the BMP and physical serial IDs based on the information actually @@ -2076,13 +2397,14 @@ public abstract class SQLQueries { @Parameter("bmp_serial_id") @Parameter("physical_serial_id") @GeneratesID - protected static final String SET_BOARD_SERIAL_IDS = - "INSERT INTO board_serial(" - + "board_id, bmp_serial_id, physical_serial_id) " - + "VALUES(:board_id, :bmp_serial_id, :physical_serial_id) " - + "ON DUPLICATE KEY UPDATE " - + "bmp_serial_id = VALUES(bmp_serial_id), " - + "physical_serial_id = VALUES(physical_serial_id)"; + protected static final String SET_BOARD_SERIAL_IDS = """ + INSERT INTO board_serial( + board_id, bmp_serial_id, physical_serial_id) + VALUES(:board_id, :bmp_serial_id, :physical_serial_id) + ON DUPLICATE KEY UPDATE + bmp_serial_id = VALUES(bmp_serial_id), + physical_serial_id = VALUES(physical_serial_id) + """; /** * Mark a read of a blacklist or ADC data as completed. @@ -2091,9 +2413,11 @@ public abstract class SQLQueries { */ @Parameter("data") @Parameter("op_id") - protected static final String COMPLETED_BOARD_INFO_READ = - "UPDATE blacklist_ops SET data = :data, completed = 1 " - + "WHERE op_id = :op_id"; + protected static final String COMPLETED_BOARD_INFO_READ = """ + UPDATE blacklist_ops + SET data = :data, completed = 1 + WHERE op_id = :op_id + """; /** * Mark a write of a blacklist as completed. @@ -2101,8 +2425,11 @@ public abstract class SQLQueries { * @see BMPController */ @Parameter("op_id") - protected static final String COMPLETED_BLACKLIST_WRITE = - "UPDATE blacklist_ops SET completed = 1 WHERE op_id = :op_id"; + protected static final String COMPLETED_BLACKLIST_WRITE = """ + UPDATE blacklist_ops + SET completed = 1 + WHERE op_id = :op_id + """; /** * Mark a read of serial data as completed. (Text same as @@ -2111,8 +2438,11 @@ public abstract class SQLQueries { * @see BMPController */ @Parameter("op_id") - protected static final String COMPLETED_GET_SERIAL_REQ = - "UPDATE blacklist_ops SET completed = 1 WHERE op_id = :op_id"; + protected static final String COMPLETED_GET_SERIAL_REQ = """ + UPDATE blacklist_ops + SET completed = 1 + WHERE op_id = :op_id + """; /** * Mark a read or write of a blacklist as failed, noting the reason. @@ -2121,8 +2451,11 @@ public abstract class SQLQueries { */ @Parameter("failure") @Parameter("op_id") - protected static final String FAILED_BLACKLIST_OP = "UPDATE blacklist_ops " - + "SET failure = :failure, completed = 1 WHERE op_id = :op_id"; + protected static final String FAILED_BLACKLIST_OP = """ + UPDATE blacklist_ops + SET failure = :failure, completed = 1 + WHERE op_id = :op_id + """; /** * Retrieve a completed request to read or write a BMP-related data for a @@ -2136,11 +2469,12 @@ public abstract class SQLQueries { @ResultColumn("data") @ResultColumn("failure") @SingleRowResult - protected static final String GET_COMPLETED_BLACKLIST_OP = - "SELECT board_id, op, data, failure, " - + "failure IS NOT NULL AS failed " - + "FROM blacklist_ops WHERE op_id = :op_id AND completed " - + "LIMIT 1"; + protected static final String GET_COMPLETED_BLACKLIST_OP = """ + SELECT board_id, op, data, failure, failure IS NOT NULL AS failed + FROM blacklist_ops + WHERE op_id = :op_id AND completed + LIMIT 1 + """; /** * Delete a blacklist request. @@ -2148,8 +2482,10 @@ public abstract class SQLQueries { * @see MachineStateControl */ @Parameter("op_id") - protected static final String DELETE_BLACKLIST_OP = - "DELETE FROM blacklist_ops WHERE op_id = :op_id"; + protected static final String DELETE_BLACKLIST_OP = """ + DELETE FROM blacklist_ops + WHERE op_id = :op_id + """; /** * Insert a request to read a board's blacklist. @@ -2158,9 +2494,11 @@ public abstract class SQLQueries { */ @Parameter("board_id") @GeneratesID - protected static final String CREATE_BLACKLIST_READ = - "INSERT INTO blacklist_ops(board_id, op, completed) " - + "VALUES(:board_id, 0, 0)"; + protected static final String CREATE_BLACKLIST_READ = """ + INSERT INTO blacklist_ops( + board_id, op, completed) + VALUES (:board_id, 0, 0) + """; /** * Insert a request to write a board's blacklist. @@ -2170,9 +2508,11 @@ public abstract class SQLQueries { @Parameter("board_id") @Parameter("blacklist") @GeneratesID - protected static final String CREATE_BLACKLIST_WRITE = - "INSERT INTO blacklist_ops(board_id, op, completed, data) " - + "VALUES(:board_id, 1, 0, :blacklist)"; + protected static final String CREATE_BLACKLIST_WRITE = """ + INSERT INTO blacklist_ops( + board_id, op, completed, data) + VALUES (:board_id, 1, 0, :blacklist) + """; /** * Insert a request to read a board's serial information. @@ -2181,9 +2521,231 @@ public abstract class SQLQueries { */ @Parameter("board_id") @GeneratesID - protected static final String CREATE_SERIAL_READ_REQ = - "INSERT INTO blacklist_ops(board_id, op, completed) " - + "VALUES(:board_id, 2, 0)"; + protected static final String CREATE_SERIAL_READ_REQ = """ + INSERT INTO blacklist_ops( + board_id, op, completed) + VALUES (:board_id, 2, 0) + """; + + /** + * Find an allocatable board with a specific board ID. (This will have been + * previously converted from some other form of board coordinates.) + * + * @see AllocatorTask + */ + @Parameter("machine_id") + @Parameter("board_id") + @ResultColumn("x") + @ResultColumn("y") + @ResultColumn("z") + @SingleRowResult + protected static final String FIND_LOCATION = """ + SELECT + x, y, z + FROM boards + WHERE boards.machine_id = :machine_id + AND boards.board_id = :board_id + AND boards.may_be_allocated + LIMIT 1 + """; + + /** Get the list of allocation tasks. */ + @Parameter("job_state") + @ResultColumn("req_id") + @ResultColumn("job_id") + @ResultColumn("num_boards") + @ResultColumn("width") + @ResultColumn("height") + @ResultColumn("board_id") + @ResultColumn("machine_id") + @ResultColumn("max_dead_boards") + @ResultColumn("max_width") + @ResultColumn("max_height") + @ResultColumn("job_state") + @ResultColumn("importance") + protected static final String GET_ALLOCATION_TASKS = """ + SELECT + job_request.req_id, + job_request.job_id, + job_request.num_boards, + job_request.width, + job_request.height, + job_request.board_id, + jobs.machine_id AS machine_id, + job_request.max_dead_boards, + machines.width AS max_width, + machines.height AS max_height, + jobs.job_state AS job_state, + job_request.importance + FROM + job_request + JOIN jobs USING (job_id) + JOIN machines USING (machine_id) + WHERE job_state = :job_state + ORDER BY importance DESC, req_id ASC + """; + + /** + * Get enabled boards with at least as many reported problems as a given + * threshold. + */ + @Parameter("threshold") + @ResultColumn("board_id") + @ResultColumn("num_reports") + @ResultColumn("x") + @ResultColumn("y") + @ResultColumn("z") + @ResultColumn("address") + protected static final String GET_REPORTED_BOARDS = """ + WITH report_counts AS ( + SELECT + board_reports.board_id, + COUNT(board_reports.report_id) AS num_reports + FROM board_reports + JOIN boards USING (board_id) + WHERE boards.functioning != 0 -- Ignore disabled boards + GROUP BY board_id) + SELECT + boards.board_id, + report_counts.num_reports, + boards.x, + boards.y, + boards.z, + boards.address + FROM + report_counts + JOIN boards USING (board_id) + WHERE report_counts.num_reports >= :threshold + """; + + /** Create a request to change the power status of a board. */ + @Parameter("job_id") + @Parameter("board_id") + @Parameter("from_state") + @Parameter("to_state") + @Parameter("power") + @Parameter("fpga_n") + @Parameter("fpga_s") + @Parameter("fpga_e") + @Parameter("fpga_w") + @Parameter("fpga_nw") + @Parameter("fpga_se") + @GeneratesID + protected static final String ISSUE_CHANGE_FOR_JOB = """ + INSERT INTO pending_changes( + job_id, board_id, from_state, to_state, + power, fpga_n, fpga_e, fpga_se, fpga_s, fpga_w, fpga_nw) + VALUES ( + :job_id, :board_id, :from_state, :to_state, + :power, :fpga_n, :fpga_e, :fpga_se, :fpga_s, :fpga_w, :fpga_nw) + """; + + /** + * Get the links of a machine that have been disabled; each link is + * described by the boards it links and the directions the link goes in at + * each end. + */ + @Parameter("machine_id") + @ResultColumn("board_1_x") + @ResultColumn("board_1_y") + @ResultColumn("board_1_z") + @ResultColumn("board_1_c") + @ResultColumn("board_1_f") + @ResultColumn("board_1_b") + @ResultColumn("board_1_addr") + @ResultColumn("dir_1") + @ResultColumn("board_2_x") + @ResultColumn("board_2_y") + @ResultColumn("board_2_z") + @ResultColumn("board_2_c") + @ResultColumn("board_2_f") + @ResultColumn("board_2_b") + @ResultColumn("board_2_addr") + @ResultColumn("dir_2") + protected static final String GET_DEAD_LINKS = """ + SELECT + -- Link end 1: board coords + direction + b1.x AS board_1_x, b1.y AS board_1_y, b1.z AS board_1_z, + bmp1.cabinet AS board_1_c, bmp1.frame AS board_1_f, + b1.board_num AS board_1_b, b1.address AS board_1_addr, + dir_1, + -- Link end 2: board coords + direction + b2.x AS board_2_x, b2.y AS board_2_y, b2.z AS board_2_z, + bmp2.cabinet AS board_2_c, bmp2.frame AS board_2_f, + b2.board_num AS board_2_b, b2.address AS board_2_addr, + dir_2 + FROM links + JOIN boards AS b1 ON board_1 = b1.board_id + JOIN boards AS b2 ON board_2 = b2.board_id + JOIN bmp AS bmp1 ON bmp1.bmp_id = b1.bmp_id + JOIN bmp AS bmp2 ON bmp2.bmp_id = b2.bmp_id + WHERE + b1.machine_id = :machine_id AND NOT live + ORDER BY board_1 ASC, board_2 ASC; + """; + + /** + * Copy jobs to the historical data DB, and return the IDs of the jobs that + * were copied (which will now be safe to delete). Only jobs that are + * already completed will ever get copied. + * + * @see AllocatorTask#tombstone() + */ + @Parameter("grace_period") + @ResultColumn("job_id") + protected static final String COPY_JOBS_TO_HISTORICAL_DATA = """ + WITH + t(now) AS (VALUES (CAST(strftime('%s','now') AS INTEGER))) + INSERT OR IGNORE INTO tombstone.jobs( + job_id, machine_id, owner, create_timestamp, + width, height, "depth", root_id, + keepalive_interval, keepalive_host, + death_reason, death_timestamp, + original_request, + allocation_timestamp, allocation_size, + machine_name, owner_name, "group", group_name) + SELECT + jobs.job_id, jobs.machine_id, jobs.owner, jobs.create_timestamp, + jobs.width, jobs.height, jobs."depth", jobs.allocated_root, + jobs.keepalive_interval, jobs.keepalive_host, + jobs.death_reason, jobs.death_timestamp, + original_request, + allocation_timestamp, allocation_size, + machines.machine_name, user_info.user_name, + groups.group_id, groups.group_name + FROM + jobs + JOIN groups USING (group_id) + JOIN machines USING (machine_id) + JOIN user_info ON jobs.owner = user_info.user_id + JOIN t + WHERE death_timestamp + :grace_period < t.now + RETURNING job_id + """; + + /** + * Copy board allocation data to the historical data DB, and return the IDs + * of the allocation records that were copied (which will now be safe to + * delete). Only jobs that are already completed will ever get copied. + * + * @see AllocatorTask#tombstone() + */ + @Parameter("grace_period") + @ResultColumn("alloc_id") + protected static final String COPY_ALLOCS_TO_HISTORICAL_DATA = """ + WITH + t(now) AS (VALUES (CAST(strftime('%s','now') AS INTEGER))) + INSERT OR IGNORE INTO tombstone.board_allocations( + alloc_id, job_id, board_id, allocation_timestamp) + SELECT + src.alloc_id, src.job_id, src.board_id, src.alloc_timestamp + FROM + old_board_allocations AS src + JOIN jobs USING (job_id) + JOIN t + WHERE jobs.death_timestamp + :grace_period < t.now + RETURNING alloc_id + """; /** * Insert a request to read a board's temperature data. @@ -2192,10 +2754,11 @@ public abstract class SQLQueries { */ @Parameter("board_id") @GeneratesID - protected static final String CREATE_TEMP_READ_REQ = - "INSERT INTO blacklist_ops(board_id, op, completed) " - + "VALUES(:board_id, 3, 0)"; - + protected static final String CREATE_TEMP_READ_REQ = """ + INSERT INTO blacklist_ops( + board_id, op, completed) + VALUES(:board_id, 3, 0) + """; /** * Read historical allocations to be written to the historical data DB. @@ -2208,10 +2771,12 @@ public abstract class SQLQueries { @ResultColumn("job_id") @ResultColumn("board_id") @ResultColumn("alloc_timestamp") - protected static final String READ_HISTORICAL_ALLOCS = - "SELECT alloc_id, job_id, board_id, alloc_timestamp " - + "FROM old_board_allocations JOIN jobs USING (job_id) " - + "WHERE jobs.death_timestamp + :grace_period < UNIX_TIMESTAMP()"; + protected static final String READ_HISTORICAL_ALLOCS = """ + SELECT alloc_id, job_id, board_id, alloc_timestamp + FROM old_board_allocations + JOIN jobs USING (job_id) + WHERE jobs.death_timestamp + :grace_period < UNIX_TIMESTAMP() + """; /** * Read historical jobs to be written to the historical data DB. @@ -2237,18 +2802,19 @@ public abstract class SQLQueries { @ResultColumn("user_name") @ResultColumn("group_id") @ResultColumn("group_name") - protected static final String READ_HISTORICAL_JOBS = - "SELECT job_id, machine_id, owner, create_timestamp, " - + "jobs.width as width, jobs.height as height, jobs.depth as depth," - + "allocated_root, keepalive_interval, keepalive_host, " - + "death_reason, death_timestamp, original_request, " - + "allocation_timestamp, allocation_size, " - + "machine_name, user_name, group_id, group_name " - + "FROM jobs " - + "JOIN user_groups USING (group_id) " - + "JOIN machines USING (machine_id) " - + "JOIN user_info ON jobs.owner = user_info.user_id " - + "WHERE death_timestamp + :grace_period < UNIX_TIMESTAMP()"; + protected static final String READ_HISTORICAL_JOBS = """ + SELECT job_id, machine_id, owner, create_timestamp, + jobs.width AS width, jobs.height AS height, jobs.depth AS depth, + allocated_root, keepalive_interval, keepalive_host, + death_reason, death_timestamp, original_request, + allocation_timestamp, allocation_size, + machine_name, user_name, group_id, group_name + FROM jobs + JOIN user_groups USING (group_id) + JOIN machines USING (machine_id) + JOIN user_info ON jobs.owner = user_info.user_id + WHERE death_timestamp + :grace_period < UNIX_TIMESTAMP() + """; /** * Write to the historical allocations database. @@ -2257,10 +2823,11 @@ public abstract class SQLQueries { @Parameter("job_id") @Parameter("board_id") @Parameter("allocation_timestamp") - protected static final String WRITE_HISTORICAL_ALLOCS = - "INSERT IGNORE INTO board_allocations( " - + "alloc_id, job_id, board_id, allocation_timestamp) " - + "VALUES(:alloc_id, :job_id, :board_id, :allocation_timestamp)"; + protected static final String WRITE_HISTORICAL_ALLOCS = """ + INSERT IGNORE INTO board_allocations( + alloc_id, job_id, board_id, allocation_timestamp) + VALUES(:alloc_id, :job_id, :board_id, :allocation_timestamp) + """; /** * Write to the historical jobs database. @@ -2284,20 +2851,21 @@ public abstract class SQLQueries { @Parameter("owner_name") @Parameter("group_id") @Parameter("group_name") - protected static final String WRITE_HISTORICAL_JOBS = - "INSERT IGNORE INTO jobs( " - + "job_id, machine_id, owner, create_timestamp, " - + "width, height, depth, root_id, " - + "keepalive_interval, keepalive_host, " - + "death_reason, death_timestamp, " - + "original_request, allocation_timestamp, allocation_size, " - + "machine_name, owner_name, group_id, group_name) " - + "VALUES(:job_id, :machine_id, :owner, :create_timestamp, " - + ":width, :height, :depth, :root_id, " - + ":keepalive_interval, :keepalive_host, " - + ":death_reason, :death_timestamp, " - + ":original_request, :allocation_timestamp, :allocation_size, " - + ":machine_name, :owner_name, :group_id, :group_name)"; + protected static final String WRITE_HISTORICAL_JOBS = """ + INSERT IGNORE INTO jobs( + job_id, machine_id, owner, create_timestamp, + width, height, depth, root_id, + keepalive_interval, keepalive_host, + death_reason, death_timestamp, + original_request, allocation_timestamp, allocation_size, + machine_name, owner_name, group_id, group_name) + VALUES(:job_id, :machine_id, :owner, :create_timestamp, + :width, :height, :depth, :root_id, + :keepalive_interval, :keepalive_host, + :death_reason, :death_timestamp, + :original_request, :allocation_timestamp, :allocation_size, + :machine_name, :owner_name, :group_id, :group_name) + """; /** * Set the NMPI session for a Job. @@ -2305,10 +2873,11 @@ public abstract class SQLQueries { @Parameter("job_id") @Parameter("session_id") @Parameter("quota_units") - protected static final String SET_JOB_SESSION = - "INSERT IGNORE INTO job_nmpi_session ( " - + "job_id, session_id, quota_units) " - + "VALUES(:job_id, :session_id, :quota_units)"; + protected static final String SET_JOB_SESSION = """ + INSERT IGNORE INTO job_nmpi_session ( + job_id, session_id, quota_units) + VALUES(:job_id, :session_id, :quota_units) + """; /** * Set the NMPI Job for a Job. @@ -2316,10 +2885,11 @@ public abstract class SQLQueries { @Parameter("job_id") @Parameter("nmpi_job_id") @Parameter("quota_units") - protected static final String SET_JOB_NMPI_JOB = - "INSERT IGNORE INTO job_nmpi_job ( " - + "job_id, nmpi_job_id, quota_units) " - + "VALUES(:job_id, :nmpi_job_id, :quota_units)"; + protected static final String SET_JOB_NMPI_JOB = """ + INSERT IGNORE INTO job_nmpi_job ( + job_id, nmpi_job_id, quota_units) + VALUES(:job_id, :nmpi_job_id, :quota_units) + """; /** * Get the NMPI Session for a Job. @@ -2327,9 +2897,11 @@ public abstract class SQLQueries { @Parameter("job_id") @ResultColumn("session_id") @ResultColumn("quota_units") - protected static final String GET_JOB_SESSION = - "SELECT session_id, quota_units FROM job_nmpi_session " - + "WHERE job_id=:job_id"; + protected static final String GET_JOB_SESSION = """ + SELECT session_id, quota_units + FROM job_nmpi_session + WHERE job_id = :job_id + """; /** * Get the NMPI Job for a Job. @@ -2337,9 +2909,11 @@ public abstract class SQLQueries { @Parameter("job_id") @ResultColumn("nmpi_job_id") @ResultColumn("quota_units") - protected static final String GET_JOB_NMPI_JOB = - "SELECT nmpi_job_id, quota_units FROM job_nmpi_job " - + "WHERE job_id=:job_id"; + protected static final String GET_JOB_NMPI_JOB = """ + SELECT nmpi_job_id, quota_units + FROM job_nmpi_job + WHERE job_id = :job_id + """; // SQL loaded from files because it is too complicated otherwise! @@ -2593,8 +3167,10 @@ public abstract class SQLQueries { protected Resource findBoardByIPAddress; /** - * Get jobs on a machine that have changes that can be processed. (A policy - * of how long after switching a board on or off is applied.) + * Get jobs on a machine that have changes that can be processed. This + * respects a machine-level policy on how long a board must be switched off + * before it can be switched on again, and how long a board must be switched + * on before it can be switched off. */ @Parameter("machine_id") @ResultColumn("job_id") @@ -2603,8 +3179,8 @@ public abstract class SQLQueries { /** * Get the set of boards at some coordinates within a triad rectangle that - * are connected (i.e., have at least one path over enableable links) to the - * root board. + * are connected (i.e., have at least one path over enableable links within + * the allocation) to the root board. * * @see AllocatorTask */ @@ -2618,56 +3194,4 @@ public abstract class SQLQueries { @ResultColumn("board_id") @Value("classpath:queries/connected_boards_at_coords.sql") protected Resource getConnectedBoards; - - /** Get the links of a machine that have been disabled. */ - @Parameter("machine_id") - @ResultColumn("board_1_x") - @ResultColumn("board_1_y") - @ResultColumn("board_1_z") - @ResultColumn("board_1_c") - @ResultColumn("board_1_f") - @ResultColumn("board_1_b") - @ResultColumn("board_1_addr") - @ResultColumn("dir_1") - @ResultColumn("board_2_x") - @ResultColumn("board_2_y") - @ResultColumn("board_2_z") - @ResultColumn("board_2_c") - @ResultColumn("board_2_f") - @ResultColumn("board_2_b") - @ResultColumn("board_2_addr") - @ResultColumn("dir_2") - @Value("classpath:queries/get_dead_links.sql") - protected Resource getDeadLinks; - - /** Get the list of allocation tasks. */ - @Parameter("job_state") - @ResultColumn("req_id") - @ResultColumn("job_id") - @ResultColumn("num_boards") - @ResultColumn("width") - @ResultColumn("height") - @ResultColumn("board_id") - @ResultColumn("machine_id") - @ResultColumn("max_dead_boards") - @ResultColumn("max_width") - @ResultColumn("max_height") - @ResultColumn("job_state") - @ResultColumn("importance") - @Value("classpath:queries/get_allocation_tasks.sql") - protected Resource getAllocationTasks; - - /** - * Get enabled boards with at least as many reported problems as a given - * threshold. - */ - @Parameter("threshold") - @ResultColumn("board_id") - @ResultColumn("num_reports") - @ResultColumn("x") - @ResultColumn("y") - @ResultColumn("z") - @ResultColumn("address") - @Value("classpath:queries/get_reported_boards.sql") - protected Resource getReportedBoards; } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/Utils.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/Utils.java index d503be040d..0d05e442cb 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/Utils.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/db/Utils.java @@ -92,8 +92,8 @@ private static String trimSQLComments(String sql) { */ public static boolean isBusy(DataAccessException exception) { var root = exception.getMostSpecificCause(); - if (root instanceof SQLException) { - return isBusy((SQLException) root); + if (root instanceof SQLException e) { + return isBusy(e); } return false; } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardCoords.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardCoords.java index 83ff08a992..170c648df6 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardCoords.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardCoords.java @@ -17,8 +17,6 @@ import static java.lang.String.format; -import java.util.Objects; - import com.google.errorprone.annotations.Immutable; import uk.ac.manchester.spinnaker.alloc.db.Row; @@ -34,73 +32,30 @@ * Basic coordinates of a board. The result of a DB query. * * @author Donal Fellows + * @param x + * Logical triad X coordinate. + * @param y + * Logical triad Y coordinate. + * @param z + * Logical triad Z coordinate. + * @param cabinet + * Physical cabinet number. + * @param frame + * Physical frame number. + * @param board + * Physical board number. May be {@code null} if the board is dead + * (e.g., because it is outright absent from the machine). + * @param address + * IP address of ethernet chip. May be {@code null} if the current + * user doesn't have permission to see the board address at this + * point, or the board is dead (e.g., because it is outright absent + * from the machine). */ @Immutable -public final class BoardCoords { - /** Logical triad X coordinate. */ - @ValidTriadX - private final int x; - - /** Logical triad Y coordinate. */ - @ValidTriadY - private final int y; - - /** Logical triad Z coordinate. */ - @ValidTriadZ - private final int z; - - /** Physical cabinet number. */ - @ValidCabinetNumber - private final int cabinet; - - /** Physical frame number. */ - @ValidFrameNumber - private final int frame; - - /** - * Physical board number. May be {@code null} if the board is dead (e.g., - * because it is outright absent from the machine). - */ - @ValidBoardNumber - private final Integer board; - - /** - * IP address of ethernet chip. May be {@code null} if the current user - * doesn't have permission to see the board address at this point, or the - * board is dead (e.g., because it is outright absent from the machine). - */ - @IPAddress(nullOK = true) - private final String address; - - /** - * Make an instance from raw results. Uncommon. - * - * @param x - * Logical triad X coordinate - * @param y - * Logical triad Y coordinate - * @param z - * Logical triad Z coordinate - * @param cabinet - * Physical cabinet number - * @param frame - * Physical frame number - * @param board - * Physical board number, or {@code null} - * @param address - * IP address of ethernet chip, or {@code null} - */ - public BoardCoords(int x, int y, int z, int cabinet, int frame, - Integer board, String address) { - this.x = x; - this.y = y; - this.z = z; - this.cabinet = cabinet; - this.frame = frame; - this.board = board; - this.address = address; - } - +public record BoardCoords(@ValidTriadX int x, @ValidTriadY int y, + @ValidTriadZ int z, @ValidCabinetNumber int cabinet, + @ValidFrameNumber int frame, @ValidBoardNumber Integer board, + @IPAddress(nullOK = true) String address) { /** * Construct a set of board coordinates from a database row that describes * them. The common constructor. @@ -111,84 +66,10 @@ public BoardCoords(int x, int y, int z, int cabinet, int frame, * Whether the {@link #address} should be shrouded. */ public BoardCoords(Row row, boolean shroudAddress) { - x = row.getInt("x"); - y = row.getInt("y"); - z = row.getInt("z"); - cabinet = row.getInt("cabinet"); - frame = row.getInt("frame"); - board = row.getInteger("board_num"); - address = shroudAddress ? null : row.getString("address"); - } - - /** - * @return Logical triad X coordinate. - */ - public int getX() { - return x; - } - - /** - * @return Logical triad Y coordinate. - */ - public int getY() { - return y; - } - - /** - * @return Logical triad Z coordinate. - */ - public int getZ() { - return z; - } - - /** - * @return Physical cabinet number. - */ - public int getCabinet() { - return cabinet; - } - - /** - * @return Physical frame number. - */ - public int getFrame() { - return frame; - } - - /** - * @return Physical board number. May be {@code null} if the board is dead - * (e.g., because it is outright absent from the machine). - */ - public Integer getBoard() { - return board; - } - - /** - * @return IP address of ethernet chip. May be {@code null} if the current - * user doesn't have permission to see the board address at this - * point, or the board is dead (e.g., because it is outright absent - * from the machine). - */ - public String getAddress() { - return address; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BoardCoords) { - var other = (BoardCoords) o; - return (x == other.x) && (y == other.y) && (z == other.z) - && (cabinet == other.cabinet) && (frame == other.frame) - && Objects.equals(board, other.board) - && Objects.equals(address, other.address); - } - return false; - } - - @Override - public int hashCode() { - // Just uses the triad coordinates - return x << 16 | y << 8 | z; + this(row.getInt("x"), row.getInt("y"), row.getInt("z"), + row.getInt("cabinet"), row.getInt("frame"), + row.getInteger("board_num"), + shroudAddress ? null : row.getString("address")); } @Override diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardIssueReport.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardIssueReport.java index b9b2ba0c66..1af8f8af92 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardIssueReport.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardIssueReport.java @@ -15,11 +15,10 @@ */ package uk.ac.manchester.spinnaker.alloc.model; -import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NON_PRIVATE; - import java.time.Instant; -import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import uk.ac.manchester.spinnaker.alloc.db.Row; import uk.ac.manchester.spinnaker.alloc.db.SQLQueries; @@ -29,21 +28,24 @@ * A report of an issue with a board. * * @author Donal Fellows + * @param id + * The report ID. + * @param boardId + * The board ID. + * @param issue + * What did they report? + * @param reporter + * Who reported it? + * @param timestamp + * When was it reported? */ -@JsonAutoDetect(setterVisibility = NON_PRIVATE) -public class BoardIssueReport { - private int id; - - private int boardId; - - private String issue; - - private String reporter; - - private Instant timestamp; - - /** Create a record. */ - public BoardIssueReport() { +public record BoardIssueReport(int id, int boardId, String issue, + String reporter, Instant timestamp) { + @JsonCreator + BoardIssueReport(@JsonProperty int id, @JsonProperty int boardId, + @JsonProperty String issue, @JsonProperty String reporter, + @JsonProperty String timestamp) { + this(id, boardId, issue, reporter, Instant.parse(timestamp)); } /** @@ -55,55 +57,8 @@ public BoardIssueReport() { */ @UsedInJavadocOnly(SQLQueries.class) public BoardIssueReport(Row row) { - id = row.getInt("report_id"); - boardId = row.getInt("board_id"); - issue = row.getString("reported_issue"); - reporter = row.getString("reporter_name"); - timestamp = row.getInstant("report_timestamp"); - } - - /** @return The report ID. */ - public int getId() { - return id; - } - - void setId(int id) { - this.id = id; - } - - /** @return The board ID. */ - public int getBoardId() { - return boardId; - } - - void setBoardId(int id) { - this.boardId = id; - } - - /** @return What did they report? */ - public String getIssue() { - return issue; - } - - void setIssue(String issue) { - this.issue = issue; - } - - /** @return Who reported it? */ - public String getReporter() { - return reporter; - } - - void setReporter(String reporter) { - this.reporter = reporter; - } - - /** @return When was it reported? */ - public Instant getTimestamp() { - return timestamp; - } - - void setTimestamp(Instant timestamp) { - this.timestamp = timestamp; + this(row.getInt("report_id"), row.getInt("board_id"), + row.getString("reported_issue"), row.getString("reporter_name"), + row.getInstant("report_timestamp")); } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardRecord.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardRecord.java index 07e38d15f5..334efb8c1e 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardRecord.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardRecord.java @@ -21,11 +21,10 @@ import java.util.ArrayList; import java.util.List; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotBlank; - import com.google.errorprone.annotations.Keep; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; import uk.ac.manchester.spinnaker.machine.board.ValidBoardNumber; import uk.ac.manchester.spinnaker.machine.board.ValidCabinetNumber; import uk.ac.manchester.spinnaker.machine.board.ValidFrameNumber; @@ -39,8 +38,8 @@ * * @author Donal Fellows */ +@JavaBean public class BoardRecord { - // TODO convert to structured form private Integer id; private Integer bmpId; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardTemperatures.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardTemperatures.java index 66be6f6bcc..92d752a2b3 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardTemperatures.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/BoardTemperatures.java @@ -20,6 +20,7 @@ /** * A board temperature. */ +// Used by JSP, so not a record public class BoardTemperatures { /** The board temperature. */ public final double boardTemperature; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/ConnectionInfo.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/ConnectionInfo.java index afe030780a..4d2b08c572 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/ConnectionInfo.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/ConnectionInfo.java @@ -18,17 +18,13 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NON_PRIVATE; import static com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY; -import java.util.Objects; - -import javax.validation.Valid; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.google.errorprone.annotations.Immutable; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.ChipLocation; -import uk.ac.manchester.spinnaker.machine.HasChipLocation; import uk.ac.manchester.spinnaker.spalloc.messages.Connection; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; import uk.ac.manchester.spinnaker.utils.validation.IPAddress; @@ -37,10 +33,12 @@ * Describes a connection by its chip and hostname. * * @see Connection + * @param chip + * The chip for the connection. + * @param hostname + * Where to connect to to talk to the chip. */ -@JsonPropertyOrder({ - "chip", "hostname" -}) +@JsonPropertyOrder({ "chip", "hostname" }) @JsonFormat(shape = ARRAY) @JsonAutoDetect(setterVisibility = NON_PRIVATE) /* @@ -49,51 +47,8 @@ */ @Immutable @UsedInJavadocOnly(Connection.class) -public final class ConnectionInfo { - @Valid - private final ChipLocation chip; - - @IPAddress - private final String hostname; - - /** - * Create. - * - * @param chip - * the chip - * @param hostname - * the host - */ - public ConnectionInfo(HasChipLocation chip, String hostname) { - this.chip = chip.asChipLocation(); - this.hostname = hostname; - } - - /** @return The chip for the connection. */ - public ChipLocation getChip() { - return chip; - } - - /** @return Where to connect to to talk to the chip. */ - public String getHostname() { - return hostname; - } - - @Override - public boolean equals(Object other) { - if (other instanceof ConnectionInfo) { - var c = (ConnectionInfo) other; - return Objects.equals(chip, c.chip) - && Objects.equals(hostname, c.hostname); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(hostname, chip); - } - +public record ConnectionInfo(@Valid ChipLocation chip, + @IPAddress String hostname) { @Override public String toString() { return "Connection(" + chip + "@" + hostname + ")"; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/DownLink.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/DownLink.java index 68ee50abdb..336ae425e2 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/DownLink.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/DownLink.java @@ -17,43 +17,35 @@ import static com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - import com.fasterxml.jackson.annotation.JsonFormat; import com.google.errorprone.annotations.Immutable; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + /** * Describes a link that is disabled. * * @author Donal Fellows + * @param end1 + * One end of the down link. + * @param end2 + * The other end of the down link. */ @Immutable @JsonFormat(shape = ARRAY) -public final class DownLink { +public record DownLink(@Valid DownLink.End end1, @Valid DownLink.End end2) { /** * Describes one end of a link that is disabled. * * @author Donal Fellows + * @param board + * On what board is this end of the link. + * @param direction + * In which direction does this end of the link go? */ @Immutable - public static final class End { - private End(BoardCoords board, Direction direction) { - this.board = board; - this.direction = direction; - } - - /** - * On what board is this end of the link. - */ - @Valid - public final BoardCoords board; - - /** - * In which direction does this end of the link go? - */ - @NotNull - public final Direction direction; + public record End(@Valid BoardCoords board, @NotNull Direction direction) { } /** @@ -70,15 +62,6 @@ private End(BoardCoords board, Direction direction) { */ public DownLink(BoardCoords board1, Direction dir1, BoardCoords board2, Direction dir2) { - end1 = new End(board1, dir1); - end2 = new End(board2, dir2); + this(new End(board1, dir1), new End(board2, dir2)); } - - /** One end of the down link. */ - @Valid - public final DownLink.End end1; - - /** The other end of the down link. */ - @Valid - public final DownLink.End end2; } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/GroupRecord.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/GroupRecord.java index 889b618954..a6590c6fb8 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/GroupRecord.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/GroupRecord.java @@ -23,18 +23,18 @@ import java.util.Map; import java.util.Optional; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Null; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Null; import uk.ac.manchester.spinnaker.alloc.db.Row; /** * The description and model of a group. POJO class; changes not automatically * reflected in the DB. */ +@JavaBean @JsonAutoDetect(setterVisibility = NON_PRIVATE) public final class GroupRecord { /** The type of a group. */ diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JavaBean.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JavaBean.java new file mode 100644 index 0000000000..473d228591 --- /dev/null +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JavaBean.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 The University of Manchester + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package uk.ac.manchester.spinnaker.alloc.model; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Marks that a particular class is used as a Java Bean because it is used as + * part of a the MVC model. Java Beans can't be records currently, as JSTL does + * not support them. + * + * @author Donal Fellows + */ +@Retention(SOURCE) +@Target(TYPE) +@interface JavaBean { + /** + * Whether this is a modifiable Java Bean. + * + * @return Whether this class has any setters. + */ + boolean modifiable() default true; +} diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JobDescription.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JobDescription.java index b6f17263cd..5478dc5eba 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JobDescription.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JobDescription.java @@ -24,9 +24,8 @@ import java.util.List; import java.util.Optional; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import uk.ac.manchester.spinnaker.machine.ValidMachineHeight; import uk.ac.manchester.spinnaker.machine.ValidMachineWidth; @@ -54,6 +53,7 @@ * * (That's actually slightly edited output from {@code spalloc-job -info}) */ +@JavaBean public class JobDescription { private int id; @@ -285,7 +285,7 @@ public void setRequestBytes(byte[] bytes) { * @return The width of the allocation in triads. 0 if not yet allocated. */ public int getTriadWidth() { - var stats = boards.stream().collect(summarizingInt(BoardCoords::getX)); + var stats = boards.stream().collect(summarizingInt(BoardCoords::x)); if (stats.getCount() < 1) { return 0; } @@ -296,7 +296,7 @@ public int getTriadWidth() { * @return The height of the allocation in triads. 0 if not yet allocated. */ public int getTriadHeight() { - var stats = boards.stream().collect(summarizingInt(BoardCoords::getY)); + var stats = boards.stream().collect(summarizingInt(BoardCoords::y)); if (stats.getCount() < 1) { return 0; } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JobListEntryRecord.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JobListEntryRecord.java index 0a5ec4ada5..9c24c93c29 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JobListEntryRecord.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/JobListEntryRecord.java @@ -23,9 +23,9 @@ import java.util.List; import java.util.Optional; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.PositiveOrZero; /** * Entry in a table of machines. The table is like this: @@ -72,6 +72,7 @@ * ... * */ +@JavaBean public class JobListEntryRecord { private int id; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineDescription.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineDescription.java index e9be060655..b7650fabfe 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineDescription.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineDescription.java @@ -21,10 +21,9 @@ import java.util.List; import java.util.Optional; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.PositiveOrZero; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.machine.board.ValidTriadHeight; import uk.ac.manchester.spinnaker.machine.board.ValidTriadWidth; import uk.ac.manchester.spinnaker.utils.MappableIterable; @@ -32,6 +31,7 @@ /** * Descriptive detail for a machine. Used for HTML generation. */ +@JavaBean public class MachineDescription { private int id; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineListEntryRecord.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineListEntryRecord.java index c91e697642..b07afb05f3 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineListEntryRecord.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineListEntryRecord.java @@ -21,9 +21,9 @@ import java.util.List; import java.util.Optional; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; /** * Entry in a table of machines. The table is like this: @@ -58,6 +58,7 @@ * ... * */ +@JavaBean public class MachineListEntryRecord { private int id; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineTagging.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineTagging.java index 156e3243dc..78f269e864 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineTagging.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MachineTagging.java @@ -23,10 +23,9 @@ import java.util.List; import java.util.Set; -import javax.validation.constraints.NotBlank; - import com.fasterxml.jackson.annotation.JsonAutoDetect; +import jakarta.validation.constraints.NotBlank; import uk.ac.manchester.spinnaker.alloc.db.Row; import uk.ac.manchester.spinnaker.alloc.db.SQLQueries; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; @@ -36,6 +35,7 @@ * * @author Donal Fellows */ +@JavaBean @JsonAutoDetect(setterVisibility = NON_PRIVATE) public class MachineTagging { @NotBlank diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MemberRecord.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MemberRecord.java index 67d7f75a89..0048e4362d 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MemberRecord.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/MemberRecord.java @@ -19,10 +19,10 @@ import java.net.URI; -import javax.validation.constraints.NotBlank; - import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.validation.constraints.NotBlank; + /** * Description of the membership of one user in one group. * @@ -30,6 +30,7 @@ * @see GroupRecord * @author Donal Fellows */ +@JavaBean @JsonInclude(NON_NULL) public class MemberRecord { private int id; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/PasswordChangeRecord.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/PasswordChangeRecord.java index a486ddf680..cf5ed4fea0 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/PasswordChangeRecord.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/PasswordChangeRecord.java @@ -17,9 +17,9 @@ import static java.util.Objects.requireNonNull; -import javax.validation.constraints.AssertFalse; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.AssertFalse; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; /** * Describes basic information about a user that they'd use to change their @@ -27,6 +27,7 @@ * * @author Donal Fellows */ +@JavaBean public class PasswordChangeRecord { private int userId; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/TagList.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/TagList.java index 05aba41a43..439c0f012e 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/TagList.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/TagList.java @@ -25,8 +25,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Payload; -import javax.validation.constraints.Pattern; +import jakarta.validation.Payload; +import jakarta.validation.constraints.Pattern; /** * Validates that a string looks like a comma-separated tag list. diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/UserRecord.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/UserRecord.java index 59021a72bd..bdf26d49c2 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/UserRecord.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/model/UserRecord.java @@ -25,16 +25,15 @@ import java.time.Instant; import java.util.Map; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Null; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; import uk.ac.manchester.spinnaker.alloc.db.Row; import uk.ac.manchester.spinnaker.alloc.db.SQLQueries; import uk.ac.manchester.spinnaker.alloc.security.TrustLevel; @@ -46,6 +45,7 @@ * this class but the service itself will not respect being asked to change * them. */ +@JavaBean @JsonAutoDetect(setterVisibility = NON_PRIVATE) public final class UserRecord { private Integer userId; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Job.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Job.java index 373572e280..9ed49d5bd2 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Job.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Job.java @@ -52,7 +52,7 @@ public Integer getId() { * @param id * the id to set */ - public void setId(final Integer id) { + public void setId(Integer id) { this.id = id; } @@ -71,7 +71,7 @@ public String getCollab() { * @param collab * the collab to set */ - public void setCollab(final String collab) { + public void setCollab(String collab) { this.collab = collab; } @@ -90,7 +90,7 @@ public String getStatus() { * @param status * the status to set */ - public void setStatus(final String status) { + public void setStatus(String status) { this.status = status; } @@ -109,7 +109,7 @@ public String getUserId() { * @param userId * the userId to set */ - public void setUserId(final String userId) { + public void setUserId(String userId) { this.userId = userId; } @@ -128,7 +128,7 @@ public ResourceUsage getResourceUsage() { * @param resourceUsage * the resourceUsage to set */ - public void setResourceUsage(final ResourceUsage resourceUsage) { + public void setResourceUsage(ResourceUsage resourceUsage) { this.resourceUsage = resourceUsage; } @@ -143,7 +143,7 @@ public void setResourceUsage(final ResourceUsage resourceUsage) { * @hidden */ @JsonAnySetter - public void set(final String name, final Object value) { + void set(String name, Object value) { // Ignore any other values } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/JobResourceUpdate.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/JobResourceUpdate.java index 188a17d718..c3b632c081 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/JobResourceUpdate.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/JobResourceUpdate.java @@ -30,19 +30,19 @@ public class JobResourceUpdate { /** * Get the count of how much resource has been used by the job. * - * @return the resourceUsage + * @return the resource usage */ public ResourceUsage getResourceUsage() { return resourceUsage; } /** - * Sets the resourceUsage. + * Sets the resource usage. * * @param resourceUsage - * the resourceUsage to set + * the resource usage to set */ - public void setResourceUsage(final ResourceUsage resourceUsage) { + public void setResourceUsage(ResourceUsage resourceUsage) { this.resourceUsage = resourceUsage; } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/NMPIv3API.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/NMPIv3API.java index b2621443d5..2cbcafe194 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/NMPIv3API.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/NMPIv3API.java @@ -19,20 +19,20 @@ import java.util.List; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; /** * The REST API for the NMPI Service. diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Project.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Project.java index 6215d8e621..124c76d3fb 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Project.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Project.java @@ -53,7 +53,7 @@ public void setQuotas(List quotas) { * @hidden */ @JsonAnySetter - public void set(final String name, final Object value) { + void set(String name, Object value) { // Ignore any other values } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Quota.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Quota.java index c43be1d01c..2d5a3e0a1d 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Quota.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/Quota.java @@ -105,7 +105,7 @@ public void setUnits(String units) { * @hidden */ @JsonAnySetter - public void set(final String name, final Object value) { + void set(String name, Object value) { // Ignore any other values } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionRequest.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionRequest.java index e0018d9b7c..8149e1db88 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionRequest.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionRequest.java @@ -55,7 +55,7 @@ public String getCollab() { * @param collab * the collab to set */ - public void setCollab(final String collab) { + public void setCollab(String collab) { this.collab = collab; } @@ -74,7 +74,7 @@ public String getUserId() { * @param userId * the userId to set */ - public void setUserId(final String userId) { + public void setUserId(String userId) { this.userId = userId; } @@ -119,7 +119,7 @@ public void setHardwareConfig(Map hardwareConfig) { * @hidden */ @JsonAnySetter - public void set(final String name, final Object value) { + void set(String name, Object value) { // Ignore any other values } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionResourceUpdate.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionResourceUpdate.java index 64a127e85c..c3eda573e9 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionResourceUpdate.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionResourceUpdate.java @@ -45,26 +45,26 @@ public String getStatus() { * @param status * the status to set */ - public void setStatus(final String status) { + public void setStatus(String status) { this.status = status; } /** * Get the count of how much resource has been used by the job. * - * @return the resourceUsage + * @return the resource usage */ public ResourceUsage getResourceUsage() { return resourceUsage; } /** - * Sets the resourceUsage. + * Sets the resource usage. * * @param resourceUsage - * the resourceUsage to set + * the resource usage to set */ - public void setResourceUsage(final ResourceUsage resourceUsage) { + public void setResourceUsage(ResourceUsage resourceUsage) { this.resourceUsage = resourceUsage; } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionResponse.java index 4ec5587094..76bf86dc2d 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/SessionResponse.java @@ -35,12 +35,12 @@ public Integer getId() { } /** - * Sets the id. + * Sets the ID. * * @param id * the id to set */ - public void setId(final Integer id) { + public void setId(Integer id) { this.id = id; } @@ -55,7 +55,7 @@ public void setId(final Integer id) { * @hidden */ @JsonAnySetter - public void set(final String name, final Object value) { + void set(String name, Object value) { // Ignore any other values } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/package-info.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/package-info.java index ef65e44c05..a0d5068fd6 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/package-info.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/nmpi/package-info.java @@ -14,6 +14,6 @@ * limitations under the License. */ /** - * Interaction with the NMPI interface. + * Model classes for interaction with the NMPI interface. */ package uk.ac.manchester.spinnaker.alloc.nmpi; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/ProxyCore.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/ProxyCore.java index 0c746c79d0..dbc7da6dbd 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/ProxyCore.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/ProxyCore.java @@ -16,8 +16,6 @@ package uk.ac.manchester.spinnaker.alloc.proxy; import static java.net.InetAddress.getByName; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.nio.charset.StandardCharsets.UTF_8; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.web.socket.CloseStatus.BAD_DATA; @@ -25,6 +23,7 @@ import static uk.ac.manchester.spinnaker.alloc.proxy.ProxyOp.CLOSE; import static uk.ac.manchester.spinnaker.alloc.proxy.ProxyOp.OPEN_UNCONNECTED; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import static uk.ac.manchester.spinnaker.utils.UnitConstants.KILOBYTE; import static uk.ac.manchester.spinnaker.utils.UnitConstants.MSEC_PER_SEC; @@ -49,7 +48,6 @@ import uk.ac.manchester.spinnaker.alloc.model.ConnectionInfo; import uk.ac.manchester.spinnaker.machine.ChipLocation; -import uk.ac.manchester.spinnaker.utils.ValueHolder; /** * The main proxy class for a particular web socket session. It's bound to a @@ -125,10 +123,10 @@ public class ProxyCore implements AutoCloseable { this.localHost = localHost; for (var ci : connections) { try { - hosts.put(ci.getChip(), getByName(ci.getHostname())); + hosts.put(ci.chip(), getByName(ci.hostname())); } catch (UnknownHostException e) { log.warn("unexpectedly unknown board address: {}", - ci.getHostname(), e); + ci.hostname(), e); } } recvFrom = Set.copyOf(hosts.values()); @@ -140,21 +138,20 @@ private interface Impl { } private Impl decode(int opcode) { - switch (ProxyOp.values()[opcode]) { - case OPEN: - return this::openConnectedChannel; - case CLOSE: - return this::closeChannel; - case MESSAGE: - return this::sendMessage; - case OPEN_UNCONNECTED: - return this::openUnconnectedChannel; - case MESSAGE_TO: - return this::sendMessageTo; - default: - log.warn("unexpected proxy opcode: {}", opcode); - throw new IllegalArgumentException("bad opcode"); - } + return switch (ProxyOp.values()[opcode]) { + case OPEN -> this::openConnectedChannel; + case CLOSE -> this::closeChannel; + case MESSAGE -> this::sendMessage; + case OPEN_UNCONNECTED -> this::openUnconnectedChannel; + case MESSAGE_TO -> this::sendMessageTo; + default -> this::unexpectedMessage; + }; + } + + private ByteBuffer unexpectedMessage(ByteBuffer message) { + int opcode = message.getInt(0); + log.error("unexpected message: {}", opcode); + return composeError(-1, new Exception("unexpected message: " + opcode)); } /** @@ -186,7 +183,7 @@ public final void handleClientMessage(ByteBuffer message) private static ByteBuffer response(ProxyOp op, int correlationId, int additionalWords) { int nWords = RESPONSE_WORDS + additionalWords; - var msg = allocate(nWords * WORD_SIZE).order(LITTLE_ENDIAN); + var msg = alloc(nWords * WORD_SIZE); msg.putInt(op.ordinal()); msg.putInt(correlationId); return msg; @@ -212,8 +209,7 @@ private static ByteBuffer composeError(int corId, Exception ex) { msg = null; continue; } - var data = allocate(WORD_SIZE + WORD_SIZE + msgBytes.length); - data.order(LITTLE_ENDIAN); + var data = alloc(WORD_SIZE + WORD_SIZE + msgBytes.length); data.putInt(ProxyOp.ERROR.ordinal()); data.putInt(corId); data.put(msgBytes); @@ -318,17 +314,15 @@ protected ByteBuffer openUnconnectedChannel(ByteBuffer message) { assertEndOfMessage(message); try { - var localAddress = new ValueHolder(); - var localPort = new ValueHolder(); - int id = openUnconnected(localAddress, localPort); - var addr = localAddress.getValue().getAddress(); + var uc = openUnconnected(); + var addr = uc.localAddr().getAddress(); log.debug("Unconnected channel local address: {}", addr); var msg = response(OPEN_UNCONNECTED, corId, ID_WORDS + IP_ADDR_AND_PORT_WORDS); - msg.putInt(id); + msg.putInt(uc.id()); msg.put(addr); - msg.putInt(localPort.getValue()); + msg.putInt(uc.localPort()); return msg; } catch (Exception e) { log.error("failed to open unconnected channel", e); @@ -336,8 +330,10 @@ protected ByteBuffer openUnconnectedChannel(ByteBuffer message) { } } - private int openUnconnected(ValueHolder localAddress, - ValueHolder localPort) throws IOException { + private record Unconnected(int id, InetAddress localAddr, int localPort) { + } + + private Unconnected openUnconnected() throws IOException { int id = idIssuer.getAsInt(); var conn = new ProxyUDPConnection(session, null, 0, id, () -> removeConnection(id), localHost); @@ -350,10 +346,7 @@ private int openUnconnected(ValueHolder localAddress, log.info("opened proxy unconnected channel {}:{} from {}:{}", session, id, who, port); - // Arrange for values to be sent out - localAddress.setValue(who); - localPort.setValue(port); - return id; + return new Unconnected(id, who, port); } /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/ProxyUDPConnection.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/ProxyUDPConnection.java index 414fb7c1dc..6d1b8dd5fd 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/ProxyUDPConnection.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/ProxyUDPConnection.java @@ -16,10 +16,9 @@ package uk.ac.manchester.spinnaker.alloc.proxy; import static java.lang.Thread.currentThread; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.connections.UDPConnection.TrafficClass.IPTOS_THROUGHPUT; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.io.IOException; import java.net.InetAddress; @@ -70,7 +69,7 @@ public class ProxyUDPConnection extends UDPConnection> { super(localHost, null, remoteHost, remotePort, IPTOS_THROUGHPUT); this.session = session; this.emergencyRemove = emergencyRemove; - workingBuffer = allocate(WORKING_BUFFER_SIZE).order(LITTLE_ENDIAN); + workingBuffer = alloc(WORKING_BUFFER_SIZE); // Fixed header for this particular connection workingBuffer.putInt(ProxyOp.MESSAGE.ordinal()); workingBuffer.putInt(id); @@ -259,11 +258,11 @@ private void mainLoop(Set recvFrom) throws IOException { continue; } // SECURITY: drop any packet not from an allocated board - if (!recvFrom.contains(packet.getAddress().getAddress())) { - log.debug("dropped packet from {}", packet.getAddress()); + if (!recvFrom.contains(packet.address().getAddress())) { + log.debug("dropped packet from {}", packet.address()); continue; } - handleReceivedMessage(packet.getByteBuffer()); + handleReceivedMessage(packet.byteBuffer()); } } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/SpinWSHandler.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/SpinWSHandler.java index 277ba98558..b32fbe8bc9 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/SpinWSHandler.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/proxy/SpinWSHandler.java @@ -15,10 +15,10 @@ */ package uk.ac.manchester.spinnaker.alloc.proxy; +import static jakarta.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE; import static java.lang.Integer.parseInt; import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.util.concurrent.Executors.newCachedThreadPool; -import static javax.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.alloc.proxy.Utils.getFieldFromTemplate; @@ -31,9 +31,6 @@ import java.util.Optional; import java.util.concurrent.ExecutorService; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.server.ServerHttpRequest; @@ -48,6 +45,8 @@ import org.springframework.web.socket.server.HandshakeInterceptor; import org.springframework.web.util.UriTemplate; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import uk.ac.manchester.spinnaker.alloc.SpallocProperties; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.Job; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AccessDeniedExceptionMapper.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AccessDeniedExceptionMapper.java index 2a4e5b5022..58be167e73 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AccessDeniedExceptionMapper.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AccessDeniedExceptionMapper.java @@ -15,19 +15,12 @@ */ package uk.ac.manchester.spinnaker.alloc.security; +import static jakarta.ws.rs.core.Response.status; +import static jakarta.ws.rs.core.Response.Status.FORBIDDEN; import static java.util.stream.Collectors.toSet; -import static javax.ws.rs.core.Response.status; -import static javax.ws.rs.core.Response.Status.FORBIDDEN; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.beans.factory.config.BeanDefinition.ROLE_SUPPORT; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - import org.slf4j.Logger; import org.springframework.context.annotation.Role; import org.springframework.security.access.AccessDeniedException; @@ -37,6 +30,12 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; import org.springframework.stereotype.Component; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; /** @@ -64,15 +63,13 @@ class AccessDeniedExceptionMapper public Response toResponse(AccessDeniedException exception) { // Actually produce useful logging; the default is ghastly! var p = req.getUserPrincipal(); - if (p instanceof AbstractAuthenticationToken) { - var who = (AbstractAuthenticationToken) p; + if (p instanceof AbstractAuthenticationToken who) { log.warn("access denied: {} : {} {}", ui.getAbsolutePath(), who.getName(), who.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(toSet())); - } else if (p instanceof OAuth2AuthenticatedPrincipal) { - var who = (OAuth2AuthenticatedPrincipal) p; + } else if (p instanceof OAuth2AuthenticatedPrincipal who) { log.warn("access denied: {} : {} {}", ui.getAbsolutePath(), who.getName(), who.getAuthorities().stream() diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AnyTypeMethodSecurityExpressionHandler.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AnyTypeMethodSecurityExpressionHandler.java index cd7285755b..128b629c2d 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AnyTypeMethodSecurityExpressionHandler.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AnyTypeMethodSecurityExpressionHandler.java @@ -54,8 +54,8 @@ public Object filter(Object target, Expression expr, || target instanceof Map || target instanceof Stream) { return super.filter(target, expr, ctx); } - if (target instanceof Optional) { - return filterOptional((Optional) target, expr, ctx); + if (target instanceof Optional opt) { + return filterOptional(opt, expr, ctx); } else { return filterOptional(Optional.of(target), expr, ctx).orElse(null); } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AppAuthTransformationFilter.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AppAuthTransformationFilter.java index 5f83a5e8f6..61eedaa982 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AppAuthTransformationFilter.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/AppAuthTransformationFilter.java @@ -22,11 +22,11 @@ import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -94,8 +94,7 @@ protected void doFilterInternal(HttpServletRequest request, private static Authentication getSavedToken(HttpSession session) { if (nonNull(session)) { var o = session.getAttribute(TOKEN); - if (o instanceof Token) { - var t = (Token) o; + if (o instanceof Token t) { if (t.isValid(session)) { return t.getAuth(); } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/BasicAuthEntryPoint.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/BasicAuthEntryPoint.java index 3771ea60cd..0895712f7a 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/BasicAuthEntryPoint.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/BasicAuthEntryPoint.java @@ -15,22 +15,21 @@ */ package uk.ac.manchester.spinnaker.alloc.security; +import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import static java.lang.String.format; import static java.util.stream.Collectors.joining; -import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import static org.slf4j.LoggerFactory.getLogger; import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.AuthProperties; /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthProviderImpl.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthProviderImpl.java index d2dc27506f..c909935225 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthProviderImpl.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthProviderImpl.java @@ -32,6 +32,7 @@ import static uk.ac.manchester.spinnaker.alloc.security.TrustLevel.USER; import static uk.ac.manchester.spinnaker.utils.OptionalUtils.ifElse; +import java.io.Serial; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -40,8 +41,8 @@ import java.util.Optional; import java.util.regex.Pattern; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -49,7 +50,6 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.security.access.intercept.RunAsUserToken; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AnonymousAuthenticationToken; @@ -97,6 +97,7 @@ * @see AuthProperties Configuration properties * @author Donal Fellows */ +@SuppressWarnings("deprecation") @Service public class LocalAuthProviderImpl extends DatabaseAwareBean implements LocalAuthenticationProvider { @@ -189,6 +190,7 @@ private void initUserIfNecessary() { } private static final class SetupException extends RuntimeException { + @Serial private static final long serialVersionUID = -3915472090182223715L; SetupException(String message) { @@ -230,28 +232,26 @@ public Authentication authenticate(Authentication auth) } try { - if (auth instanceof UsernamePasswordAuthenticationToken) { - return authenticateDirect( - (UsernamePasswordAuthenticationToken) auth); - } else if (auth instanceof OAuth2AuthenticationToken) { + if (auth instanceof UsernamePasswordAuthenticationToken userpass) { + return authenticateDirect(userpass); + } else if (auth instanceof OAuth2AuthenticationToken oauth) { /* * Technically, at this point we're already authenticated as * we've checked that the token from Keycloak is valid. We still * have to take an authorization decision though. */ - var user = ((OAuth2AuthenticationToken) auth).getPrincipal(); + var user = oauth.getPrincipal(); return authorizeOpenId( authProps.getOpenid().getUsernamePrefix() + user.getAttribute(PREFERRED_USERNAME), user.getAttribute(SUB), new OriginatingCredential(user), auth.getAuthorities()); - } else if (auth instanceof BearerTokenAuthentication) { + } else if (auth instanceof BearerTokenAuthentication bearerAuth) { /* * Technically, at this point we're already authenticated as * we've checked that the token from Keycloak is valid. We still * have to take an authorization decision though. */ - var bearerAuth = (BearerTokenAuthentication) auth; var token = bearerAuth.getToken(); return authorizeOpenId( authProps.getOpenid().getUsernamePrefix() @@ -300,7 +300,7 @@ public Authentication updateAuthentication(HttpServletRequest req, /** The classes that we know we don't ever want to handle. */ private static final Class[] UNSUPPORTED_AUTH_TOKEN_CLASSES = { AnonymousAuthenticationToken.class, RememberMeAuthenticationToken.class, - RunAsUserToken.class, TestingAuthenticationToken.class + TestingAuthenticationToken.class }; @Override @@ -337,6 +337,7 @@ static boolean isUnsupportedAuthTokenClass(Class cls) { private static final class PerformedUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken implements AlreadyDoneMarker { + @Serial private static final long serialVersionUID = -3164620207079316329L; PerformedUsernamePasswordAuthenticationToken(String name, @@ -424,19 +425,14 @@ private Authentication authorizeOpenId(String name, String subject, } /** Holds either a {@link OAuth2User} or a {@link Jwt}. */ - private static final class OriginatingCredential { - private final OAuth2User user; - - private final OAuth2AccessToken token; - + private record OriginatingCredential(OAuth2User user, + OAuth2AccessToken token) { OriginatingCredential(OAuth2User user) { - this.user = requireNonNull(user); - this.token = null; + this(requireNonNull(user), null); } OriginatingCredential(OAuth2AccessToken token) { - this.user = null; - this.token = requireNonNull(token); + this(null, requireNonNull(token)); } @Override @@ -452,6 +448,7 @@ public String toString() { private static final class OpenIDDerivedAuthenticationToken extends AbstractAuthenticationToken implements OpenIDUserAware, AlreadyDoneMarker { + @Serial private static final long serialVersionUID = 970898019896708267L; private final String who; @@ -708,6 +705,7 @@ void apply(int userId) { @Immutable static final class CollabratoryAuthority extends SimpleGrantedAuthority { + @Serial private static final long serialVersionUID = 4964366746649162092L; private final String collabratory; @@ -724,6 +722,7 @@ String getCollabratory() { @Immutable static final class OrganisationAuthority extends SimpleGrantedAuthority { + @Serial private static final long serialVersionUID = 8260068770503054502L; private final String organisation; @@ -847,29 +846,19 @@ public void mapAuthorities(OidcUserAuthority user, mapAuthorities("userinfo", user.getUserInfo(), ga); } + /** + * Auth succeeded. + * + * @param userId + * The user ID + * @param trustLevel + * The trust level. + * @param passInfo + * The encoded password. + */ @Immutable - private static final class LocalAuthResult { - final int userId; - - final TrustLevel trustLevel; - - final String passInfo; - - /** - * Auth succeeded. - * - * @param u - * The user ID - * @param t - * The trust level. - * @param ep - * The encoded password. - */ - LocalAuthResult(int u, TrustLevel t, String ep) { - userId = u; - trustLevel = requireNonNull(t); - passInfo = requireNonNull(ep); - } + private record LocalAuthResult(int userId, TrustLevel trustLevel, + String passInfo) { } /** @@ -902,9 +891,9 @@ private boolean authLocalAgainstDB(String username, String password, checkPassword(username, password, details, queries); // Succeeded; finalize into external form return queries.transaction(() -> { - queries.noteLoginSuccessForUser(details.userId); + queries.noteLoginSuccessForUser(details.userId()); // Convert tiered trust level to grant form - details.trustLevel.getGrants() + details.trustLevel().getGrants() .forEach(authorities::add); return true; }); @@ -982,16 +971,16 @@ private static Optional lookUpUserDetails(String username, * The username (now validated as existing). * @param password * The user-provided password that we're checking. - * @param queries - * How to access the DB. * @param details * The results of looking up the user + * @param queries + * How to access the DB. */ private void checkPassword(String username, String password, LocalAuthResult details, AuthQueries queries) { - if (!passServices.matchPassword(password, details.passInfo)) { + if (!passServices.matchPassword(password, details.passInfo())) { queries.transaction(() -> { - queries.noteLoginFailureForUser(details.userId, username); + queries.noteLoginFailureForUser(details.userId(), username); log.info("login failure for {}: bad password", username); throw new BadCredentialsException("bad password"); }); @@ -1112,16 +1101,14 @@ private void checkSubject(String username, String subject, private void inflateGroup(GrantedAuthority ga, List collabs, List orgs, AuthQueries queries) { - if (ga instanceof CollabratoryAuthority) { - var collab = (CollabratoryAuthority) ga; + if (ga instanceof CollabratoryAuthority collab) { var collab1 = collab.getCollabratory(); if (queries.createGroup(collab1, COLLABRATORY, quotaProps.getDefaultCollabQuota())) { log.info("created collabratory '{}'", collab1); } collabs.add(collab.getCollabratory()); - } else if (ga instanceof OrganisationAuthority) { - var org = (OrganisationAuthority) ga; + } else if (ga instanceof OrganisationAuthority org) { var org1 = org.getOrganisation(); if (queries.createGroup(org1, ORGANISATION, quotaProps.getDefaultOrgQuota())) { diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthenticationProvider.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthenticationProvider.java index 98dbac5f71..3e0ad88621 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthenticationProvider.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthenticationProvider.java @@ -19,8 +19,6 @@ import java.util.Collection; -import javax.servlet.http.HttpServletRequest; - import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; @@ -30,6 +28,7 @@ import com.google.errorprone.annotations.RestrictedApi; +import jakarta.servlet.http.HttpServletRequest; import uk.ac.manchester.spinnaker.alloc.ForTestingOnly; /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/MyAuthenticationFailureHandler.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/MyAuthenticationFailureHandler.java index edbda9d744..d3532e7fb2 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/MyAuthenticationFailureHandler.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/MyAuthenticationFailureHandler.java @@ -23,10 +23,6 @@ import java.io.IOException; import java.time.Instant; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Role; @@ -36,6 +32,9 @@ import com.fasterxml.jackson.databind.json.JsonMapper; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import uk.ac.manchester.spinnaker.alloc.SpallocProperties.AuthProperties; @Component @@ -64,25 +63,9 @@ public void onAuthenticationFailure(HttpServletRequest request, message += ": " + e.getLocalizedMessage(); } mapper.writeValue(response.getOutputStream(), - new AuthFailureObject(message)); + new AuthFailureObject(message, now())); } - static class AuthFailureObject { - private String message; - - private Instant timestamp; - - AuthFailureObject(String message) { - this.message = message; - this.timestamp = now(); - } - - public String getMessage() { - return message; - } - - public Instant getTimestamp() { - return timestamp; - } + record AuthFailureObject(String message, Instant timestamp) { } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/PasswordServices.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/PasswordServices.java index 2a489449c2..bccdbc1881 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/PasswordServices.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/PasswordServices.java @@ -20,12 +20,12 @@ import java.security.SecureRandom; -import javax.annotation.PostConstruct; - import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; +import jakarta.annotation.PostConstruct; + /** * Misc services related to password handling. * diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Permit.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Permit.java index 55b202d969..08c634b1a3 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Permit.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Permit.java @@ -24,6 +24,7 @@ import java.io.NotSerializableException; import java.io.ObjectOutputStream; +import java.io.Serial; import java.util.Collection; import java.util.List; import java.util.function.Supplier; @@ -59,7 +60,7 @@ public final class Permit { * @param context * The originating security context. */ - public Permit(javax.ws.rs.core.SecurityContext context) { + public Permit(jakarta.ws.rs.core.SecurityContext context) { authorities = STDAUTH.stream().filter(context::isUserInRole) .map(SimpleGrantedAuthority::new).collect(toUnmodifiableList()); admin = is(authorities, GRANT_ADMIN); @@ -189,6 +190,7 @@ public void setAuthenticated(boolean isAuthenticated) { } } + @Serial private void writeObject(ObjectOutputStream out) throws NotSerializableException { throw new NotSerializableException("not actually serializable"); diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/SecurityConfig.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/SecurityConfig.java index 5b1132abaf..dc03608587 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/SecurityConfig.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/SecurityConfig.java @@ -55,7 +55,7 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.GrantedAuthority; @@ -92,7 +92,7 @@ */ @EnableWebSecurity @Role(ROLE_APPLICATION) -@EnableGlobalMethodSecurity(prePostEnabled = true) +@EnableMethodSecurity(prePostEnabled = true) @UsedInJavadocOnly(PreAuthorize.class) public class SecurityConfig { private static final Logger log = getLogger(SecurityConfig.class); @@ -215,13 +215,13 @@ private String oidcPath(String suffix) { * If anything goes wrong with setting up. */ private void defineAccessPolicy(HttpSecurity http) throws Exception { - http.authorizeRequests() + http.authorizeHttpRequests() // General metadata pages require ADMIN access - .antMatchers(urlMaker.serviceUrl("info*"), + .requestMatchers(urlMaker.serviceUrl("info*"), urlMaker.serviceUrl("info/**")) .hasRole("ADMIN") // Login process and static resources are available to all - .antMatchers(urlMaker.systemUrl("login*"), + .requestMatchers(urlMaker.systemUrl("login*"), urlMaker.systemUrl("perform_*"), oidcPath("**"), urlMaker.systemUrl("error"), urlMaker.systemUrl("resources/*")) @@ -409,9 +409,8 @@ GrantedAuthoritiesMapper userAuthoritiesMapper() { * returns each scope as a GrantedAuthority, which we don't care * about. */ - if (authority instanceof OidcUserAuthority) { - localAuthProvider.mapAuthorities( - (OidcUserAuthority) authority, mappedAuthorities); + if (authority instanceof OidcUserAuthority a) { + localAuthProvider.mapAuthorities(a, mappedAuthorities); } mappedAuthorities.add(authority); }); diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/SimpleGrantedAuthority.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/SimpleGrantedAuthority.java index c871dcaf1f..3f5de355eb 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/SimpleGrantedAuthority.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/SimpleGrantedAuthority.java @@ -15,15 +15,22 @@ */ package uk.ac.manchester.spinnaker.alloc.security; +import java.io.Serial; + import org.springframework.security.core.GrantedAuthority; import com.google.errorprone.annotations.Immutable; +import uk.ac.manchester.spinnaker.alloc.security.LocalAuthProviderImpl.CollabratoryAuthority; +import uk.ac.manchester.spinnaker.alloc.security.LocalAuthProviderImpl.OrganisationAuthority; + /** * Contains a single basic role grant. */ @Immutable -class SimpleGrantedAuthority implements GrantedAuthority { +sealed class SimpleGrantedAuthority implements GrantedAuthority + permits CollabratoryAuthority, OrganisationAuthority { + @Serial private static final long serialVersionUID = 7765648523730760900L; private final String role; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Token.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Token.java index 36072cffae..e0a3085e6a 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Token.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Token.java @@ -15,28 +15,23 @@ */ package uk.ac.manchester.spinnaker.alloc.security; -import java.io.Serializable; - -import javax.servlet.http.HttpSession; - import org.springframework.security.core.Authentication; +import jakarta.servlet.http.HttpSession; + /** * A saved authentication token. Categorically only ever valid in the session * for which it was created; if the session changes, it cannot be reused. * * @author Donal Fellows + * @param id + * The session ID + * @param auth + * The authentication token */ -final class Token implements Serializable { - private static final long serialVersionUID = -439034988839648948L; - - private final String id; - - private final Authentication auth; - +record Token(String id, Authentication auth) { Token(HttpSession s, Authentication a) { - this.auth = a; - this.id = s.getId(); + this(s.getId(), a); } boolean isValid(HttpSession s) { diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Utils.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Utils.java index 0b287a1f1e..1a86c030c7 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Utils.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/security/Utils.java @@ -73,8 +73,8 @@ public static X509TrustManager trustManager(KeyStore truststore) var tmf = TrustManagerFactory.getInstance(getDefaultAlgorithm()); tmf.init(truststore); for (var tm : tmf.getTrustManagers()) { - if (tm instanceof X509TrustManager) { - return (X509TrustManager) tm; + if (tm instanceof X509TrustManager x509tm) { + return x509tm; } } return null; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/BackgroundSupport.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/BackgroundSupport.java index afca0083a4..1e397fe237 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/BackgroundSupport.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/BackgroundSupport.java @@ -19,12 +19,11 @@ import java.util.concurrent.Executor; -import javax.ws.rs.container.AsyncResponse; -import javax.ws.rs.core.Response; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.core.Response; import uk.ac.manchester.spinnaker.alloc.security.Permit; import uk.ac.manchester.spinnaker.alloc.web.RequestFailedException.NotFound; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/CreateJobRequest.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/CreateJobRequest.java index 14b19aab51..caf912fb4b 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/CreateJobRequest.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/CreateJobRequest.java @@ -20,17 +20,17 @@ import java.time.Duration; import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.AssertFalse; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.errorprone.annotations.Keep; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertFalse; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.machine.board.ValidBoardNumber; import uk.ac.manchester.spinnaker.machine.board.ValidCabinetNumber; import uk.ac.manchester.spinnaker.machine.board.ValidFrameNumber; @@ -45,93 +45,73 @@ * A request to create a job. * * @author Donal Fellows + * @param owner + * Who owns the job. Ignored when the job is submitted by a + * non-admin. + * @param group + * What group will the job be accounted against; the owner + * must be a member of the group. If {@code null}, the + * single group that the owner is a member of will be used (with it + * being an error for that to not exist or not be unique). Only one + * of group, {@link #nmpiCollab} or {@link #nmpiJobId} must be + * non-{@code null}, but all can be {@code null}. + * @param nmpiCollab + * Which NMPI Collab the job will be accounted against; the owner + * must be a member of the Collab. A session will be created + * in the Collab and this will be accounted against. Only one of + * {@link #group}, nmpiCollab or {@link #nmpiJobId} must be + * non-{@code null}, but all can be {@code null}. + * @param nmpiJobId + * Which NMPI Job the job will be accounted against; the owner + * must be able to update the NMPI Job. Only the quota of + * the NMPI Job will be updated at the end of the job, as will the + * local quota of the Collab of the NMPI Job. Only one of + * {@link #group}, {@link #nmpiCollab} or nmpiJobId must be + * non-{@code null}, but all can be {@code null}. + * @param keepaliveInterval + * How long after a keepalive message will the job be auto-deleted? + * Required. Must be between 30 and 300 seconds. + * @param numBoards + * The number of boards to allocate. May be {@code null} to either + * use the default (1) or to let one of the other selectors + * ({@link #dimensions}, {@link #board}) make the choice. + * @param dimensions + * The dimensions of rectangle of triads of boards to allocate. May + * be {@code null} to let one of the other selectors + * ({@link #numBoards}, {@link #board}) make the choice. + * @param board + * The specific board to allocate. May be {@code null} to let one of + * the other selectors ({@link #numBoards}, {@link #dimensions}) make + * the choice. + * @param machineName + * Which machine to allocate on. This and {@link #tags} are mutually + * exclusive, but at least one must be given. + * @param tags + * The tags to select which machine to allocate on. This and + * {@link #machineName} are mutually exclusive, but at least one must + * be given. + * @param maxDeadBoards + * The maximum number of dead boards allowed in a rectangular + * allocation. Note that the allocation engine might increase this if + * it decides to overallocate. Defaults to {@code 0}. */ -@SuppressWarnings("checkstyle:visibilitymodifier") -public class CreateJobRequest { - /** - * Who owns the job. Ignored when the job is submitted by a non-admin. - */ - public String owner; - - /** - * What group will the job be accounted against; the owner must be - * a member of the group. If {@code null}, the single group that the owner - * is a member of will be used (with it being an error for that to not exist - * or not be unique). Only one of group, {@link #nmpiCollab} or - * {@link #nmpiJobId} must be {@code non-null}, but all can be {@code null}. - */ - public String group; - - /** - * Which NMPI Collab the job will be accounted against; the owner - * must be a member of the Collab. A session will be created in - * the Collab and this will be accounted against. Only one of - * {@link #group}, nmpiCollab or {@link #nmpiJobId} must be - * {@code non-null}, but all can be {@code null}. - */ - public String nmpiCollab; - - /** - * Which NMPI Job the job will be accounted against; the owner must - * be able to update the NMPI Job. Only the quota of the NMPI Job will be - * updated at the end of the job, as will the local quota of the Collab of - * the NMPI Job. Only one of {@link #group}, {@link #nmpiCollab} or - * nmpiJobId must be {@code non-null}, but all can be {@code null}. - */ - public Integer nmpiJobId; - - /** - * How long after a keepalive message will the job be auto-deleted? - * Required. Must be between 30 and 300 seconds. - */ - @NotNull(message = "keepalive-interval is required") - public Duration keepaliveInterval; - - /** - * The number of boards to allocate. May be {@code null} to either use the - * default (1) or to let one of the other selectors ({@link #dimensions}, - * {@link #board}) make the choice. - */ - @Positive(message = "number of boards must be at least 1 if given") - public Integer numBoards; - - /** - * The dimensions of rectangle of triads of boards to allocate. May be - * {@code null} to let one of the other selectors ({@link #numBoards}, - * {@link #board}) make the choice. - */ - @Valid - public Dimensions dimensions; - - /** - * The specific board to allocate. May be {@code null} to let one of the - * other selectors ({@link #numBoards}, {@link #dimensions}) make the - * choice. - */ - @Valid - public SpecificBoard board; - - /** - * Which machine to allocate on. This and {@link #tags} are mutually - * exclusive, but at least one must be given. - */ - public String machineName; - - /** - * The tags to select which machine to allocate on. This and - * {@link #machineName} are mutually exclusive, but at least one must be - * given. - */ - public List<@NotBlank(message = "tags must not be blank") String> tags; - - /** - * The maximum number of dead boards allowed in a rectangular allocation. - * Note that the allocation engine might increase this if it decides to - * overallocate. Defaults to {@code 0}. - */ - @PositiveOrZero(message = "max-dead-boards may not be negative") - public Integer maxDeadBoards; - +public record CreateJobRequest(@JsonProperty String owner, + @JsonProperty String group, + @JsonProperty("nmpi-collab") String nmpiCollab, + @Positive(message = "negative NMPI job IDs are unsupported") // + @JsonProperty("nmpi-job-id") Integer nmpiJobId, + @JsonProperty("keepalive-interval") + @NotNull(message = "keepalive-interval is " + + "required") Duration keepaliveInterval, + @JsonProperty("num-boards") @Positive(message = "number of boards " + + "must be at least 1 if given") Integer numBoards, + @JsonProperty @Valid Dimensions dimensions, + @JsonProperty @Valid SpecificBoard board, + @JsonProperty("machine-name") String machineName, + @JsonProperty List<@NotBlank(// + message = "tags must not be blank") String> tags, + @JsonProperty("max-dead-boards") @PositiveOrZero(message = // + "max-dead-boards may not be negative") Integer maxDeadBoards) { // Extended validation @Keep @@ -148,6 +128,20 @@ private boolean isMachineNameInsane() { return nonNull(machineName) && machineName.isBlank(); } + @Keep + @JsonIgnore + @AssertFalse(message = "group, if given, must be non-blank") + private boolean isGroupInsane() { + return nonNull(group) && group.isBlank(); + } + + @Keep + @JsonIgnore + @AssertFalse(message = "nmpi-collab, if given, must be non-blank") + private boolean isCollabInsane() { + return nonNull(nmpiCollab) && nmpiCollab.isBlank(); + } + @Keep @JsonIgnore @AssertTrue( @@ -163,6 +157,24 @@ private boolean isOverLocated() { return count <= 1; } + @Keep + @JsonIgnore + @AssertTrue(message = "only at most one of group, nmpi-collab and " + + "nmpi-job-id can be given") + private boolean isGroupLocatableInAtMostOneWayOnly() { + int count = 0; + if (nonNull(group)) { + count++; + } + if (nonNull(nmpiCollab)) { + count++; + } + if (nonNull(nmpiJobId)) { + count++; + } + return count <= 1; + } + private static final Duration MIN_KEEPALIVE = Duration.parse("PT30S"); private static final Duration MAX_KEEPALIVE = Duration.parse("PT300S"); @@ -180,47 +192,41 @@ private boolean isKeepaliveIntervalInRange() { && keepaliveInterval.compareTo(MIN_KEEPALIVE) >= 0); } - /** Describes a request for an allocation of given dimensions. */ - public static class Dimensions { - /** The width of the rectangle of boards to allocate, in triads. */ - @ValidTriadWidth - public int width; - - /** The height of the rectangle of boards to allocate, in triads. */ - @ValidTriadHeight - public int height; + /** + * Describes a request for an allocation of given dimensions. + * + * @param width + * The width of the rectangle of boards to allocate, in triads. + * @param height + * The height of the rectangle of boards to allocate, in triads. + */ + public record Dimensions(@ValidTriadWidth int width, + @ValidTriadHeight int height) { } - /** Describes a request for a specific board. */ - public static class SpecificBoard { - /** The X triad coordinate of the board. */ - @ValidTriadX - public Integer x; - - /** The Y triad coordinate of the board. */ - @ValidTriadY - public Integer y; - - /** The Z triad coordinate of the board. */ - @ValidTriadZ - public Integer z; - - /** The physical cabinet number of the board. */ - @ValidCabinetNumber - public Integer cabinet; - - /** The physical frame number of the board. */ - @ValidFrameNumber - public Integer frame; - - /** The physical board number of the board. */ - @ValidBoardNumber - public Integer board; - - /** The IP address of the board. */ - @IPAddress(nullOK = true, message = "address must be an IP address") - public String address; - + /** + * Describes a request for a specific board. + * + * @param x + * The X triad coordinate of the board. + * @param y + * The Y triad coordinate of the board. + * @param z + * The Z triad coordinate of the board. + * @param cabinet + * The physical cabinet number of the board. + * @param frame + * The physical frame number of the board. + * @param board + * The physical board number of the board. + * @param address + * The IP address of the board. + */ + public record SpecificBoard(@ValidTriadX Integer x, @ValidTriadY Integer y, + @ValidTriadZ Integer z, @ValidCabinetNumber Integer cabinet, + @ValidFrameNumber Integer frame, @ValidBoardNumber Integer board, + @IPAddress(nullOK = true, message = "address must be " + + "an IP address") String address) { @JsonIgnore private boolean isTriadValid() { return nonNull(x) && nonNull(y) && nonNull(z); diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/CreateJobResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/CreateJobResponse.java index d6704b993c..cda8de87ee 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/CreateJobResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/CreateJobResponse.java @@ -19,29 +19,25 @@ import java.net.URI; -import javax.ws.rs.core.UriInfo; - import com.fasterxml.jackson.annotation.JsonInclude; import com.google.errorprone.annotations.Immutable; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI; /** * Describes a newly-created job. * * @author Donal Fellows + * @param jobId + * The ID of the job. Probably should be ignored. + * @param jobRef + * The link to the job. Clients should not make this themselves. */ @Immutable -public class CreateJobResponse { - /** The ID of the job. Probably should be ignored. */ - public final int jobId; - - /** The link to the job. Clients should not make this themselves. */ - @JsonInclude(NON_NULL) - public final URI jobRef; - +public record CreateJobResponse(int jobId, @JsonInclude(NON_NULL) URI jobRef) { CreateJobResponse(SpallocAPI.Job j, UriInfo ui) { - jobId = j.getId(); - jobRef = ui.getRequestUriBuilder().path("{id}").build(j.getId()); + this(j.getId(), + ui.getRequestUriBuilder().path("{id}").build(j.getId())); } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/IssueReportRequest.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/IssueReportRequest.java index b513a37f02..c3f6ee6551 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/IssueReportRequest.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/IssueReportRequest.java @@ -19,13 +19,12 @@ import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotBlank; - import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.errorprone.annotations.Keep; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.board.ValidBoardNumber; import uk.ac.manchester.spinnaker.machine.board.ValidCabinetNumber; @@ -39,55 +38,43 @@ * A request to report an issue with some boards. * * @author Donal Fellows + * @param issue + * What the problem is believed to be. + * @param boards + * Describes the boards that have the issue. */ -@SuppressWarnings("checkstyle:visibilitymodifier") -public class IssueReportRequest { - /** What the problem is believed to be. */ - @NotBlank(message = "an issue description must be given") - public String issue; - - /** Describes the boards that have the issue. */ - public List<@Valid ReportedBoard> boards; - - /** Describes a board that has an issue. */ - public static class ReportedBoard { - - /** The machine containing the board. */ - @NotBlank - public String machine; - - /** The location of the chip within the reporting allocation. */ - @Valid - public ChipLocation chip; - - /** The X triad coordinate of the board. */ - @ValidTriadX - public Integer x; - - /** The Y triad coordinate of the board. */ - @ValidTriadY - public Integer y; - - /** The Z triad coordinate of the board. */ - @ValidTriadZ - public Integer z; - - /** The physical cabinet number of the board. */ - @ValidCabinetNumber - public Integer cabinet; - - /** The physical frame number of the board. */ - @ValidFrameNumber - public Integer frame; - - /** The physical board number of the board. */ - @ValidBoardNumber - public Integer board; - - /** The IP address of the board. */ - @IPAddress(nullOK = true, message = "address must be an IP address") - public String address; - +public record IssueReportRequest( + @NotBlank(message = "an issue description must be given") String issue, + List<@Valid ReportedBoard> boards) { + /** + * Describes a board that has an issue. + * + * @param machine + * The machine containing the board. + * @param chip + * The location of the chip within the reporting allocation. + * @param x + * The X triad coordinate of the board. + * @param y + * The Y triad coordinate of the board. + * @param z + * The Z triad coordinate of the board. + * @param cabinet + * The physical cabinet number of the board. + * @param frame + * The physical frame number of the board. + * @param board + * The physical board number of the board. + * @param address + * The IP address of the board. + */ + public record ReportedBoard( + @NotBlank String machine, @Valid ChipLocation chip, + @ValidTriadX Integer x, @ValidTriadY Integer y, + @ValidTriadZ Integer z, @ValidCabinetNumber Integer cabinet, + @ValidFrameNumber Integer frame, @ValidBoardNumber Integer board, + @IPAddress(nullOK = true, message = "address must be " + + "an IP address") String address) { @JsonIgnore private boolean isChipValid() { return nonNull(chip); diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/IssueReportResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/IssueReportResponse.java index 46448571a6..cbca7092ad 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/IssueReportResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/IssueReportResponse.java @@ -21,16 +21,10 @@ * Describes whether an issue with a board was reported successfully. * * @author Donal Fellows + * @param action + * What immediate action will be taken. Typically "{@code noted}" or + * "{@code taken out of service}". */ @Immutable -public class IssueReportResponse { - /** - * What immediate action will be taken. Typically "{@code noted}" or - * "{@code taken out of service}". - */ - public final String action; - - IssueReportResponse(String action) { - this.action = action; - } +public record IssueReportResponse(String action) { } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/JobStateResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/JobStateResponse.java index dcf3b3e399..e8ef8c867f 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/JobStateResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/JobStateResponse.java @@ -26,13 +26,12 @@ import java.net.URI; import java.time.Instant; -import javax.ws.rs.core.UriInfo; - import org.springframework.dao.DataAccessException; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.json.JsonMapper; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.Job; import uk.ac.manchester.spinnaker.alloc.model.JobState; import uk.ac.manchester.spinnaker.alloc.proxy.SpinWSHandler; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/ListJobsResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/ListJobsResponse.java index 3aee474922..21527cbf43 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/ListJobsResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/ListJobsResponse.java @@ -22,10 +22,9 @@ import java.net.URI; import java.util.List; -import javax.ws.rs.core.UriInfo; - import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.Jobs; /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachinePower.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachinePower.java index d35cb5c5ac..7b2720bea0 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachinePower.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachinePower.java @@ -15,11 +15,10 @@ */ package uk.ac.manchester.spinnaker.alloc.web; -import javax.validation.constraints.NotNull; - import com.fasterxml.jackson.annotation.JsonProperty; import com.google.errorprone.annotations.Immutable; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.alloc.model.PowerState; /** @@ -27,26 +26,10 @@ * of it allocated to a job), or a state that the user wants us to switch into. * * @author Donal Fellows + * @param power the machine power state */ @Immutable -public class MachinePower { - @NotNull(message = "power must be specified") - private final PowerState power; - - /** - * Make an instance. - * - * @param power - * the machine power state - */ - public MachinePower( - @JsonProperty(value = "power", defaultValue = "OFF") - PowerState power) { - this.power = power; - } - - /** @return the machine power state */ - public PowerState getPower() { - return power; - } +public record MachinePower( + @JsonProperty(value = "power", defaultValue = "OFF") + @NotNull(message = "power must be specified") PowerState power) { } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachineResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachineResponse.java index 51c74b5405..ab0b3f1498 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachineResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachineResponse.java @@ -25,10 +25,9 @@ import java.net.URI; import java.util.List; -import javax.ws.rs.core.UriInfo; - import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI; import uk.ac.manchester.spinnaker.alloc.model.BoardCoords; import uk.ac.manchester.spinnaker.alloc.model.DownLink; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachinesResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachinesResponse.java index 54e0973ef2..b5d7dc91a9 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachinesResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/MachinesResponse.java @@ -15,16 +15,14 @@ */ package uk.ac.manchester.spinnaker.alloc.web; +import static java.util.stream.Collectors.toUnmodifiableList; import static uk.ac.manchester.spinnaker.utils.CollectionUtils.copy; import java.net.URI; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Set; - -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.Machine; import uk.ac.manchester.spinnaker.alloc.model.BoardCoords; import uk.ac.manchester.spinnaker.alloc.model.DownLink; @@ -34,59 +32,48 @@ * {@link BriefMachineDescription}s. * * @author Donal Fellows + * @param machines + * The list of machines known to the service. */ -public final class MachinesResponse { +public record MachinesResponse(List machines) { /** * A brief, summary description of a machine. * * @author Donal Fellows + * @param name + * The name of the machine. + * @param tags + * The tags of the machine. + * @param uri + * The URI to the machine. + * @param width + * The width of the machine, in triads. + * @param height + * The height of the machine, in triads. + * @param deadBoards + * The dead boards on the machine. + * @param deadLinks + * The dead links on the machine. */ - public static final class BriefMachineDescription { - /** The name of the machine. */ - public final String name; - - /** The tags of the machine. */ - public final List tags; - - /** The URI to the machine. */ - public final URI uri; - - /** The width of the machine, in triads. */ - public final int width; - - /** The height of the machine, in triads. */ - public final int height; - - /** The dead boards on the machine. */ - public final List deadBoards; - - /** The dead links on the machine. */ - public final List deadLinks; - - private BriefMachineDescription(String name, URI uri, int width, - int height, Set tags, List deadBoards, - List deadLinks) { - this.name = name; - this.uri = uri; - this.width = width; - this.height = height; - this.tags = List.copyOf(tags); - this.deadBoards = copy(deadBoards); - this.deadLinks = copy(deadLinks); - } + public record BriefMachineDescription(String name, List tags, + URI uri, int width, int height, List deadBoards, + List deadLinks) { } - /** The list of machines known to the service. */ - public final List machines; - MachinesResponse(Map machines, UriInfo ui) { - var mlist = new ArrayList(machines.size()); + this(makeBriefDescriptions(machines, ui)); + } + + private static List makeBriefDescriptions( + Map machines, UriInfo ui) { var ub = ui.getAbsolutePathBuilder().path("{name}"); - machines.forEach((name, - machine) -> mlist.add(new BriefMachineDescription(name, - ub.build(name), machine.getWidth(), machine.getHeight(), - machine.getTags(), machine.getDeadBoards(), - machine.getDownLinks()))); - this.machines = copy(mlist); + return machines.entrySet().stream() + .map(e -> new BriefMachineDescription(e.getKey(), + List.copyOf(e.getValue().getTags()), + ub.build(e.getKey()), e.getValue().getWidth(), + e.getValue().getHeight(), + copy(e.getValue().getDeadBoards()), + copy(e.getValue().getDownLinks()))) + .collect(toUnmodifiableList()); } } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/RequestFailedException.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/RequestFailedException.java index 748cb189b0..3c6623b1e2 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/RequestFailedException.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/RequestFailedException.java @@ -15,27 +15,29 @@ */ package uk.ac.manchester.spinnaker.alloc.web; +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; +import static jakarta.ws.rs.core.Response.noContent; +import static jakarta.ws.rs.core.Response.status; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.GONE; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; +import static jakarta.ws.rs.core.Response.Status.NO_CONTENT; +import static jakarta.ws.rs.core.Response.Status.Family.SERVER_ERROR; import static java.util.Objects.nonNull; -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; -import static javax.ws.rs.core.Response.noContent; -import static javax.ws.rs.core.Response.status; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; -import static javax.ws.rs.core.Response.Status.GONE; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; -import static javax.ws.rs.core.Response.Status.NOT_FOUND; -import static javax.ws.rs.core.Response.Status.NO_CONTENT; -import static javax.ws.rs.core.Response.Status.Family.SERVER_ERROR; import static org.slf4j.LoggerFactory.getLogger; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import java.io.Serial; import org.slf4j.Logger; import org.springframework.stereotype.Component; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + /** * Thrown to indicate various sorts of problems with the service. Very much like * a {@link WebApplicationException} except with a different handling strategy. @@ -43,6 +45,7 @@ * @author Donal Fellows */ public class RequestFailedException extends RuntimeException { + @Serial private static final long serialVersionUID = -7522760691720854101L; /** The status code. */ @@ -98,8 +101,8 @@ public RequestFailedException(Throwable cause) { */ Response toResponse() { var cause = getCause(); - if (cause instanceof WebApplicationException) { - return ((WebApplicationException) cause).getResponse(); + if (cause instanceof WebApplicationException waex) { + return waex.getResponse(); } else if (cause != null) { // Be careful about what bits are extracted from message var cls = cause.getClass().getName().replaceFirst("^.*[.]", "") @@ -133,6 +136,7 @@ private void log(Logger log) { /** A resource is no longer believed to exist. */ public static class ItsGone extends RequestFailedException { + @Serial private static final long serialVersionUID = 3774531853141947270L; /** @@ -146,6 +150,7 @@ public ItsGone(String message) { /** A resource cannot be located. */ public static class NotFound extends RequestFailedException { + @Serial private static final long serialVersionUID = 5991697173204757030L; /** @@ -169,6 +174,7 @@ public NotFound(String message, Throwable cause) { /** The client provided bad arguments in a request. */ public static class BadArgs extends RequestFailedException { + @Serial private static final long serialVersionUID = 7916573155067333350L; /** @@ -182,6 +188,7 @@ public BadArgs(String message) { /** The response is empty. */ public static class EmptyResponse extends RequestFailedException { + @Serial private static final long serialVersionUID = -2944836034264700912L; /** Create an instance. */ diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/ServiceDescription.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/ServiceDescription.java index 34eb6d662d..aa1f88c7ff 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/ServiceDescription.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/ServiceDescription.java @@ -22,13 +22,12 @@ import java.net.URI; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.core.UriInfo; - import org.springframework.security.web.csrf.CsrfToken; import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.messages.model.Version; /** diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceAPI.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceAPI.java index c550f9b21f..00c5e81f0c 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceAPI.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceAPI.java @@ -16,8 +16,8 @@ package uk.ac.manchester.spinnaker.alloc.web; import static io.swagger.v3.oas.annotations.enums.ParameterIn.PATH; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; import static uk.ac.manchester.spinnaker.alloc.security.SecurityConfig.IS_READER; import static uk.ac.manchester.spinnaker.alloc.security.SecurityConfig.IS_USER; import static uk.ac.manchester.spinnaker.alloc.web.DocConstants.T_JOB; @@ -41,29 +41,6 @@ import static uk.ac.manchester.spinnaker.alloc.web.WebServiceComponentNames.SERV; import static uk.ac.manchester.spinnaker.alloc.web.WebServiceComponentNames.WAIT; -import javax.servlet.http.HttpServletRequest; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -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.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.container.AsyncResponse; -import javax.ws.rs.container.Suspended; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.core.UriInfo; - import org.apache.cxf.jaxrs.model.wadl.Description; import org.springframework.security.access.prepost.PreAuthorize; @@ -75,6 +52,28 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.servers.Server; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.container.Suspended; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.machine.ValidX; import uk.ac.manchester.spinnaker.machine.ValidY; import uk.ac.manchester.spinnaker.machine.board.ValidBoardNumber; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceAPIImplBuilder.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceAPIImplBuilder.java index 0ac02082c9..ec0d866250 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceAPIImplBuilder.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceAPIImplBuilder.java @@ -15,19 +15,15 @@ */ package uk.ac.manchester.spinnaker.alloc.web; +import static jakarta.ws.rs.core.Response.accepted; +import static jakarta.ws.rs.core.Response.noContent; import static java.util.Objects.isNull; -import static javax.ws.rs.core.Response.accepted; -import static javax.ws.rs.core.Response.noContent; import static org.slf4j.LoggerFactory.getLogger; import static org.springframework.beans.factory.config.BeanDefinition.ROLE_APPLICATION; import static org.springframework.beans.factory.config.BeanDefinition.ROLE_SUPPORT; import java.util.Optional; -import javax.ws.rs.container.AsyncResponse; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -37,6 +33,9 @@ import com.fasterxml.jackson.databind.json.JsonMapper; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.SpallocProperties; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.BoardLocation; @@ -230,7 +229,7 @@ public void setMachinePower(MachinePower reqBody, } bgAction(response, permit, () -> { j.access(caller); - allocation().setPower(reqBody.getPower()); + allocation().setPower(reqBody.power()); return accepted().build(); }); } diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceImpl.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceImpl.java index ce62c6d230..e7b7530759 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceImpl.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SpallocServiceImpl.java @@ -21,11 +21,11 @@ import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.stream.Collectors.toList; -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; -import static javax.ws.rs.core.Response.created; -import static javax.ws.rs.core.Response.ok; -import static javax.ws.rs.core.Response.status; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; +import static jakarta.ws.rs.core.Response.created; +import static jakarta.ws.rs.core.Response.ok; +import static jakarta.ws.rs.core.Response.status; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.CreateBoard.address; import static uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.CreateBoard.physical; @@ -38,12 +38,12 @@ import java.util.HashMap; import java.util.Optional; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Path; -import javax.ws.rs.container.AsyncResponse; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.core.UriInfo; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.core.UriInfo; import org.slf4j.Logger; import org.springframework.beans.factory.ObjectProvider; @@ -214,9 +214,11 @@ private static String trim(String str) { /** * Select one of the ways to create a job based on the request parameters. - * Note this will pick the first non-null of (group, nmpiCollabId, - * nmpiJobId) from req to determine the spalloc call to make (also - * acceptable if all are null). + *

+ * Note that this will pick the first non-{@code null} of + * (group, nmpiCollabId, nmpiJobId) from req to determine the spalloc call + * to make (also acceptable if all are {@code null}). Validation + * should ensure that at most one of those is non-{@code null}. * * @param req * The request details. @@ -228,50 +230,33 @@ private static String trim(String str) { */ private Optional createJob(CreateJobRequest req, CreateDescriptor crds) throws JsonProcessingException { - if (!isNull(req.group)) { - return core.createJobInGroup(trim(req.owner), trim(req.group), crds, - req.machineName, req.tags, req.keepaliveInterval, - mapper.writeValueAsBytes(req)); - } - if (!isNull(req.nmpiCollab)) { - return core.createJobInCollabSession(trim(req.owner), - trim(req.nmpiCollab), crds, - req.machineName, req.tags, req.keepaliveInterval, - mapper.writeValueAsBytes(req)); - } - if (!isNull(req.nmpiJobId)) { - return core.createJobForNMPIJob(trim(req.owner), - req.nmpiJobId, crds, - req.machineName, req.tags, req.keepaliveInterval, + if (!isNull(req.group())) { + return core.createJobInGroup(trim(req.owner()), trim(req.group()), + crds, req.machineName(), req.tags(), + req.keepaliveInterval(), mapper.writeValueAsBytes(req)); + } else if (!isNull(req.nmpiCollab())) { + return core.createJobInCollabSession(trim(req.owner()), + trim(req.nmpiCollab()), crds, req.machineName(), req.tags(), + req.keepaliveInterval(), mapper.writeValueAsBytes(req)); + } else if (!isNull(req.nmpiJobId())) { + return core.createJobForNMPIJob(trim(req.owner()), req.nmpiJobId(), + crds, req.machineName(), req.tags(), + req.keepaliveInterval(), mapper.writeValueAsBytes(req)); + } else { + return core.createJob(trim(req.owner()), crds, req.machineName(), + req.tags(), req.keepaliveInterval(), mapper.writeValueAsBytes(req)); } - return core.createJob(trim(req.owner), crds, - req.machineName, req.tags, req.keepaliveInterval, - mapper.writeValueAsBytes(req)); } @Override public void createJob(CreateJobRequest req, UriInfo ui, SecurityContext security, AsyncResponse response) { - var crds = validateAndApplyDefaultsToJobRequest(req, security); - - // Ensure we only have at most one "group" specifier (0 also fine). - var nonNullGroups = 0; - var items = new Object[] { - req.group, req.nmpiCollab, req.nmpiJobId - }; - for (Object item : items) { - if (!isNull(item)) { - nonNullGroups += 1; - } - } - if (nonNullGroups > 1) { - response.resume(status(BAD_REQUEST).type(TEXT_PLAIN).entity( - "At most one of group, nmpiCollabId or nmpiJobId" - + " can be specified").build()); - } + var r = validateCreateJobNonSizeAttrs(req, security); + var crds = validateAndApplyDefaultsToJobRequest(r, security); + bgAction(response, () -> ifElse( - createJob(req, crds), + createJob(r, crds), job -> created(ui.getRequestUriBuilder().path("{id}") .build(job.getId())) .entity(new CreateJobResponse(job, ui)).build(), @@ -280,86 +265,96 @@ public void createJob(CreateJobRequest req, UriInfo ui, .entity("out of quota").build())); } - private CreateDescriptor validateAndApplyDefaultsToJobRequest( - CreateJobRequest req, SecurityContext security) throws BadArgs { + private CreateJobRequest validateCreateJobNonSizeAttrs(CreateJobRequest req, + SecurityContext security) { if (isNull(req)) { throw new BadArgs("request must be supplied"); } + var owner = req.owner(); if (!security.isUserInRole("ADMIN") && !security.isUserInRole("NMPI_EXEC") - && !isNull(req.owner) && !req.owner.isBlank()) { + && !isNull(owner) && !owner.isBlank()) { throw new BadArgs("Only admin and NMPI users can specify an owner"); } - if (isNull(req.owner) || req.owner.isBlank()) { - req.owner = security.getUserPrincipal().getName(); + if (isNull(owner) || owner.isBlank()) { + owner = security.getUserPrincipal().getName(); } - if (isNull(req.owner) || req.owner.isBlank()) { + if (isNull(owner) || owner.isBlank()) { throw new BadArgs( "request must be connected to an identified owner"); } - req.owner = req.owner.strip(); + owner = owner.strip(); var ka = properties.getKeepalive(); - if (isNull(req.keepaliveInterval) - || req.keepaliveInterval.compareTo(ka.getMin()) < 0) { + if (isNull(req.keepaliveInterval()) + || req.keepaliveInterval().compareTo(ka.getMin()) < 0) { throw new BadArgs( "keepalive interval must be at least " + ka.getMin()); } - if (req.keepaliveInterval.compareTo(ka.getMax()) > 0) { + if (req.keepaliveInterval().compareTo(ka.getMax()) > 0) { throw new BadArgs( "keepalive interval must be no more than " + ka.getMax()); } - if (isNull(req.tags)) { - req.tags = new ArrayList<>(); - if (isNull(req.machineName)) { - req.tags.add("default"); + var tags = req.tags(); + if (isNull(tags)) { + tags = new ArrayList<>(); + if (isNull(req.machineName())) { + tags.add("default"); } } - if (nonNull(req.machineName) && !req.tags.isEmpty()) { + if (nonNull(req.machineName()) && !tags.isEmpty()) { throw new BadArgs( "must not specify machine name and tags together"); } - if (isNull(req.maxDeadBoards)) { - req.maxDeadBoards = 0; - } else if (req.maxDeadBoards < 0) { + return new CreateJobRequest(owner, req.group(), req.nmpiCollab(), + req.nmpiJobId(), req.keepaliveInterval(), req.numBoards(), + req.dimensions(), req.board(), req.machineName(), tags, + req.maxDeadBoards()); + } + + private CreateDescriptor validateAndApplyDefaultsToJobRequest( + CreateJobRequest req, SecurityContext security) throws BadArgs { + var maxDead = req.maxDeadBoards(); + if (isNull(maxDead)) { + maxDead = 0; + } else if (maxDead < 0) { throw new BadArgs( "the maximum number of dead boards must not be negative"); } - if (nonNull(req.numBoards)) { - return new CreateNumBoards(req.numBoards, req.maxDeadBoards); - } else if (nonNull(req.dimensions)) { - if (nonNull(req.board)) { + if (nonNull(req.numBoards())) { + return new CreateNumBoards(req.numBoards(), maxDead); + } else if (nonNull(req.dimensions())) { + var size = req.dimensions(); + var specific = req.board(); + if (nonNull(specific)) { // Both dimensions AND board; rooted rectangle - if (nonNull(req.board.x)) { - return new CreateDimensionsAt(req.dimensions.width, - req.dimensions.height, req.board.x, req.board.y, - req.board.z, req.maxDeadBoards); - } else if (nonNull(req.board.cabinet)) { - return CreateDimensionsAt.physical(req.dimensions.width, - req.dimensions.height, req.board.cabinet, - req.board.frame, req.board.board, - req.maxDeadBoards); + if (nonNull(specific.x())) { + return new CreateDimensionsAt(size.width(), size.height(), + specific.x(), specific.y(), specific.z(), maxDead); + } else if (nonNull(specific.cabinet())) { + return CreateDimensionsAt.physical(size.width(), + size.height(), specific.cabinet(), specific.frame(), + specific.board(), maxDead); } else { - return new CreateDimensionsAt(req.dimensions.width, - req.dimensions.height, req.board.address, - req.maxDeadBoards); + return new CreateDimensionsAt(size.width(), size.height(), + specific.address(), maxDead); } } - return new CreateDimensions(req.dimensions.width, - req.dimensions.height, req.maxDeadBoards); - } else if (nonNull(req.board)) { - if (nonNull(req.board.x)) { - return triad(req.board.x, req.board.y, req.board.z); - } else if (nonNull(req.board.cabinet)) { - return physical(req.board.cabinet, req.board.frame, - req.board.board); + return new CreateDimensions(size.width(), size.height(), maxDead); + } else if (nonNull(req.board())) { + var specific = req.board(); + if (nonNull(specific.x())) { + return triad(specific.x(), specific.y(), specific.z()); + } else if (nonNull(specific.cabinet())) { + return physical(specific.cabinet(), specific.frame(), + specific.board()); } else { - return address(req.board.address); + return address(specific.address()); } } else { // It's a single board diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SubMachineResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SubMachineResponse.java index b1a505bfbc..7b891b9621 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SubMachineResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SubMachineResponse.java @@ -24,10 +24,9 @@ import java.net.URI; import java.util.List; -import javax.ws.rs.core.UriInfo; - import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.SubMachine; import uk.ac.manchester.spinnaker.alloc.model.ConnectionInfo; import uk.ac.manchester.spinnaker.spalloc.messages.BoardCoordinates; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SystemController.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SystemController.java index d5d99be3d8..82bb1540b7 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SystemController.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SystemController.java @@ -20,10 +20,6 @@ import java.security.Principal; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; - import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.web.bind.annotation.GetMapping; @@ -34,6 +30,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.alloc.model.JobDescription; import uk.ac.manchester.spinnaker.alloc.model.JobListEntryRecord; import uk.ac.manchester.spinnaker.alloc.model.MachineDescription; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SystemControllerImpl.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SystemControllerImpl.java index 859642f9a5..de9947561d 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SystemControllerImpl.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/SystemControllerImpl.java @@ -43,10 +43,6 @@ import java.security.Principal; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; - import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; @@ -66,6 +62,9 @@ import com.google.errorprone.annotations.Keep; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.alloc.ServiceConfig.URLPathMaker; import uk.ac.manchester.spinnaker.alloc.ServiceVersion; import uk.ac.manchester.spinnaker.alloc.admin.UserControl; diff --git a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/WhereIsResponse.java b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/WhereIsResponse.java index d79a8034ba..01c6578ce7 100644 --- a/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/WhereIsResponse.java +++ b/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/web/WhereIsResponse.java @@ -22,10 +22,9 @@ import java.net.URI; -import javax.ws.rs.core.UriInfo; - import com.fasterxml.jackson.annotation.JsonInclude; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.BoardLocation; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.spalloc.messages.BoardCoordinates; diff --git a/SpiNNaker-allocserv/src/main/resources/queries/allocation_connected.sql b/SpiNNaker-allocserv/src/main/resources/queries/allocation_connected.sql index 0e4b7a8b85..a8a56d90a5 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/allocation_connected.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/allocation_connected.sql @@ -12,6 +12,11 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- +-- Count the number of connected boards (i.e., have at least one path over +-- enabled links to the root board of the allocation) within a rectangle of +-- triads. The triads are taken as being full depth. + WITH RECURSIVE args(machine_id, x, y, width, height) AS ( SELECT :machine_id, :x, :y, :width, :height), diff --git a/SpiNNaker-allocserv/src/main/resources/queries/connected_boards_at_coords.sql b/SpiNNaker-allocserv/src/main/resources/queries/connected_boards_at_coords.sql index a1a5047a21..4b30db170f 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/connected_boards_at_coords.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/connected_boards_at_coords.sql @@ -12,6 +12,11 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- +-- Get the set of boards at some coordinates within a triad rectangle that +-- are connected (i.e., have at least one path over enableable links within +-- the allocation) to the root board. + WITH RECURSIVE args(machine_id, x, y, z, width, height, depth) AS ( SELECT :machine_id, :x, :y, :z, :width, :height, :depth), diff --git a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_global_chip.sql b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_global_chip.sql index 4d426f88b8..6668fe7baa 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_global_chip.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_global_chip.sql @@ -12,6 +12,10 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- +-- Locate a board (using a full set of coordinates) based on global chip +-- coordinates. + SELECT -- IDs boards.board_id, diff --git a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_ip_address.sql b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_ip_address.sql index 010350832b..c7e407d8a8 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_ip_address.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_ip_address.sql @@ -12,6 +12,10 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- +-- Locate a board (using a full set of coordinates) based on the IP address +-- of its ethernet chip. + SELECT -- IDs boards.board_id, diff --git a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_job_chip.sql b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_job_chip.sql index 2f80db0b6f..e8977fbf90 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_job_chip.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_job_chip.sql @@ -12,6 +12,10 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- +-- Locate a board (using a full set of coordinates) based on allocation-local +-- chip coordinates. + WITH args(job, root, x, y) AS (SELECT :job_id, :board_id, :x, :y), -- Boards that are allocated to the job diff --git a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_logical_coords.sql b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_logical_coords.sql index 3723540ce9..a119190c93 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_logical_coords.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_logical_coords.sql @@ -12,6 +12,10 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- +-- Locate a board (using a full set of coordinates) based on logical triad +-- coordinates. + SELECT -- IDs boards.board_id, diff --git a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_physical_coords.sql b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_physical_coords.sql index e06824c58f..5368c712ce 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_physical_coords.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/find_board_by_physical_coords.sql @@ -12,6 +12,10 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- +-- Locate a board (using a full set of coordinates) based on physical +-- cabinet-frame-board coordinates. + SELECT -- IDs boards.board_id, diff --git a/SpiNNaker-allocserv/src/main/resources/queries/find_location.sql b/SpiNNaker-allocserv/src/main/resources/queries/find_location.sql deleted file mode 100644 index f3f50ce90e..0000000000 --- a/SpiNNaker-allocserv/src/main/resources/queries/find_location.sql +++ /dev/null @@ -1,20 +0,0 @@ --- Copyright (c) 2021 The University of Manchester --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- https://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -SELECT - x, y, z -FROM boards -WHERE boards.machine_id = :machine_id - AND boards.board_id = :board_id - AND boards.may_be_allocated; diff --git a/SpiNNaker-allocserv/src/main/resources/queries/find_rectangle.sql b/SpiNNaker-allocserv/src/main/resources/queries/find_rectangle.sql index b86d0fb96e..1a97996aa3 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/find_rectangle.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/find_rectangle.sql @@ -12,6 +12,8 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- + WITH RECURSIVE -- Name the arguments for sanity args(width, height, machine_id, max_dead_boards) AS ( diff --git a/SpiNNaker-allocserv/src/main/resources/queries/find_rectangle_at.sql b/SpiNNaker-allocserv/src/main/resources/queries/find_rectangle_at.sql index 0f0426850d..2df2aadab9 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/find_rectangle_at.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/find_rectangle_at.sql @@ -12,6 +12,8 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- + WITH RECURSIVE -- Name the arguments for sanity args(root_id, width, height, machine_id, max_dead_boards) AS ( diff --git a/SpiNNaker-allocserv/src/main/resources/queries/get_allocation_tasks.sql b/SpiNNaker-allocserv/src/main/resources/queries/get_allocation_tasks.sql deleted file mode 100644 index 73d3c4deaf..0000000000 --- a/SpiNNaker-allocserv/src/main/resources/queries/get_allocation_tasks.sql +++ /dev/null @@ -1,34 +0,0 @@ --- Copyright (c) 2021 The University of Manchester --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- https://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - --- Get current allocation tasks -SELECT - job_request.req_id, - job_request.job_id, - job_request.num_boards, - job_request.width, - job_request.height, - job_request.board_id, - jobs.machine_id AS machine_id, - job_request.max_dead_boards, - machines.width AS max_width, - machines.height AS max_height, - jobs.job_state AS job_state, - job_request.importance -FROM - job_request - JOIN jobs USING (job_id) - JOIN machines USING (machine_id) -WHERE job_state = :job_state -ORDER BY importance DESC, req_id ASC; diff --git a/SpiNNaker-allocserv/src/main/resources/queries/get_dead_links.sql b/SpiNNaker-allocserv/src/main/resources/queries/get_dead_links.sql deleted file mode 100644 index 5db92a3a23..0000000000 --- a/SpiNNaker-allocserv/src/main/resources/queries/get_dead_links.sql +++ /dev/null @@ -1,43 +0,0 @@ --- Copyright (c) 2021 The University of Manchester --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- https://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - --- Get the dead links of a machine; each link is described by the boards it --- links and the directions the link goes in at each end. -SELECT - -- Link end 1: board coords + direction - b1.x AS board_1_x, - b1.y AS board_1_y, - b1.z AS board_1_z, - bmp1.cabinet AS board_1_c, - bmp1.frame AS board_1_f, - b1.board_num AS board_1_b, - b1.address AS board_1_addr, - dir_1, - -- Link end 2: board coords + direction - b2.x AS board_2_x, - b2.y AS board_2_y, - b2.z AS board_2_z, - bmp2.cabinet AS board_2_c, - bmp2.frame AS board_2_f, - b2.board_num AS board_2_b, - b2.address AS board_2_addr, - dir_2 -FROM links - JOIN boards AS b1 ON board_1 = b1.board_id - JOIN boards AS b2 ON board_2 = b2.board_id - JOIN bmp AS bmp1 ON bmp1.bmp_id = b1.bmp_id - JOIN bmp AS bmp2 ON bmp2.bmp_id = b2.bmp_id -WHERE - b1.machine_id = :machine_id AND NOT live -ORDER BY board_1 ASC, board_2 ASC; diff --git a/SpiNNaker-allocserv/src/main/resources/queries/get_jobs_with_changes.sql b/SpiNNaker-allocserv/src/main/resources/queries/get_jobs_with_changes.sql index 0988f16ab5..5a478b7c38 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/get_jobs_with_changes.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/get_jobs_with_changes.sql @@ -12,6 +12,12 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +-- -------------------------------------------------------------------------- +-- Get jobs on a machine that have changes that can be processed. This +-- respects a machine-level policy on how long a board must be switched off +-- before it can be switched on again, and how long a board must be switched +-- on before it can be switched off. + WITH -- Arguments and current timestamp args(machine_id, now) AS ( diff --git a/SpiNNaker-allocserv/src/main/resources/queries/get_reported_boards.sql b/SpiNNaker-allocserv/src/main/resources/queries/get_reported_boards.sql deleted file mode 100644 index dc70c8bdc4..0000000000 --- a/SpiNNaker-allocserv/src/main/resources/queries/get_reported_boards.sql +++ /dev/null @@ -1,34 +0,0 @@ --- Copyright (c) 2021 The University of Manchester --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- https://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - --- Get boards with more problem reports than a critical threshold -WITH report_counts AS ( - SELECT - board_reports.board_id, - COUNT(board_reports.report_id) AS num_reports - FROM board_reports - JOIN boards USING (board_id) - WHERE boards.functioning != 0 -- Ignore disabled boards - GROUP BY board_id) -SELECT - boards.board_id, - report_counts.num_reports, - boards.x, - boards.y, - boards.z, - boards.address -FROM - report_counts - JOIN boards USING (board_id) -WHERE report_counts.num_reports >= :threshold; diff --git a/SpiNNaker-allocserv/src/main/resources/queries/issue_change_for_job.sql b/SpiNNaker-allocserv/src/main/resources/queries/issue_change_for_job.sql deleted file mode 100644 index 9495796e9c..0000000000 --- a/SpiNNaker-allocserv/src/main/resources/queries/issue_change_for_job.sql +++ /dev/null @@ -1,20 +0,0 @@ --- Copyright (c) 2021 The University of Manchester --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- https://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. - -INSERT INTO pending_changes( - job_id, board_id, from_state, to_state, - power, fpga_n, fpga_e, fpga_se, fpga_s, fpga_w, fpga_nw) -VALUES ( - :job_id, :board_id, :from_state, :to_state, - :power, :fpga_n, :fpga_e, :fpga_se, :fpga_s, :fpga_w, :fpga_nw); diff --git a/SpiNNaker-allocserv/src/main/resources/queries/perimeter.sql b/SpiNNaker-allocserv/src/main/resources/queries/perimeter.sql index 753630cb98..f56a1965dc 100644 --- a/SpiNNaker-allocserv/src/main/resources/queries/perimeter.sql +++ b/SpiNNaker-allocserv/src/main/resources/queries/perimeter.sql @@ -12,9 +12,14 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -WITH +-- -------------------------------------------------------------------------- +-- Get the links on the perimeter of the allocation to a job. The perimeter +-- is defined as being the live links between a board that is part of the +-- allocation and a board that is not. + +WITH bs AS ( -- Boards that are allocated to the job - bs AS (SELECT board_id FROM boards WHERE allocated_job = :job_id) + SELECT board_id FROM boards WHERE allocated_job = :job_id) SELECT board_1 AS board_id, dir_1 AS direction FROM links WHERE board_1 IN (SELECT board_id FROM bs) AND live diff --git a/SpiNNaker-allocserv/src/main/resources/spalloc.sql b/SpiNNaker-allocserv/src/main/resources/spalloc.sql index 656256fe14..2bc0fe9058 100644 --- a/SpiNNaker-allocserv/src/main/resources/spalloc.sql +++ b/SpiNNaker-allocserv/src/main/resources/spalloc.sql @@ -397,7 +397,7 @@ CREATE TABLE IF NOT EXISTS pending_changes ( CREATE TABLE IF NOT EXISTS blacklist_ops ( op_id INTEGER PRIMARY KEY AUTOINCREMENT, - board_id INTEGER UNIQUE NOT NULL + board_id INTEGER NOT NULL CONSTRAINT "blacklist_ops.board_id -> boards.board_id" REFERENCES boards(board_id) ON DELETE RESTRICT, op INTEGER NOT NULL, -- What we plan to do (NonBootOperations ID) diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/board.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/board.jsp index c976340461..8de2753efa 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/board.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/board.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/createuser.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/createuser.jsp index 903f65ff1e..b01c33bb66 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/createuser.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/createuser.jsp @@ -1,5 +1,5 @@ <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%-- Copyright (c) 2021 The University of Manchester diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/footer.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/footer.jsp index 732f355f57..6eb9cbf41a 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/footer.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/footer.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%-- Copyright (c) 2021 The University of Manchester diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/groupdetails.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/groupdetails.jsp index 1b047b694e..c223b3598f 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/groupdetails.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/groupdetails.jsp @@ -1,8 +1,8 @@ <%@ page trimDirectiveWhitespaces="true" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> +<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%-- diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/listgroups.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/listgroups.jsp index 08ba7da44f..f8eae583bd 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/listgroups.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/listgroups.jsp @@ -1,5 +1,5 @@ <%@ page trimDirectiveWhitespaces="true" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%-- Copyright (c) 2022 The University of Manchester diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/listusers.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/listusers.jsp index 7b379ab2b0..12465a3126 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/listusers.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/listusers.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%-- Copyright (c) 2021 The University of Manchester diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/machine.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/machine.jsp index b688bc3af1..e144d72dd6 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/machine.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/machine.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="c" uri="jakarta.tags.core" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/userdetails.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/userdetails.jsp index 264663bfc0..303017a71d 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/userdetails.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/admin/userdetails.jsp @@ -1,6 +1,6 @@ <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> +<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%-- diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/basicfooter.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/basicfooter.jsp index 5b1476364f..d1ad0751f1 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/basicfooter.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/basicfooter.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%-- Copyright (c) 2021 The University of Manchester diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/data/jobdetails_obj.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/data/jobdetails_obj.jsp index a86e0b695e..91cc8d4247 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/data/jobdetails_obj.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/data/jobdetails_obj.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%@ taglib prefix="json" uri="http://www.atg.com/taglibs/json" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%-- diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/data/machinedetails_obj.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/data/machinedetails_obj.jsp index 4d40d08ac2..e82a7f2d85 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/data/machinedetails_obj.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/data/machinedetails_obj.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%@ taglib prefix="json" uri="http://www.atg.com/taglibs/json" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/erroroccurred.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/erroroccurred.jsp index 963127672d..30d92cde49 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/erroroccurred.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/erroroccurred.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%-- Copyright (c) 2021 The University of Manchester diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/head.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/head.jsp index 8e6e3e5e6f..cb36c70a42 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/head.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/head.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%-- Copyright (c) 2021 The University of Manchester diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/index.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/index.jsp index 2b1e06a114..8ab626b9f3 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/index.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/index.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%-- diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/jobdetails.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/jobdetails.jsp index d6ee1b026b..6723c71970 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/jobdetails.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/jobdetails.jsp @@ -1,6 +1,6 @@ <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%-- Copyright (c) 2021 The University of Manchester diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/listjobs.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/listjobs.jsp index aa3a1af38d..1d184f2c49 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/listjobs.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/listjobs.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%-- diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/listmachines.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/listmachines.jsp index 602e64c2d1..89f7656448 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/listmachines.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/listmachines.jsp @@ -1,5 +1,5 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> +<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%-- diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/login.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/login.jsp index 4262810fd1..d109fdfb54 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/login.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/login.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <%-- diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/machinedetails.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/machinedetails.jsp index 0aa7388fbb..82a70bd09e 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/machinedetails.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/machinedetails.jsp @@ -1,7 +1,7 @@ <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> -<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> +<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %> <%-- Copyright (c) 2021 The University of Manchester diff --git a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/password.jsp b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/password.jsp index 398ea1f8da..34b4a2c0e5 100644 --- a/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/password.jsp +++ b/SpiNNaker-allocserv/src/main/webapp/WEB-INF/views/password.jsp @@ -1,4 +1,4 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ taglib prefix="c" uri="jakarta.tags.core"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <%-- diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/SupportQueries.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/SupportQueries.java index 7713bc4a22..32c204f88f 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/SupportQueries.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/SupportQueries.java @@ -32,42 +32,58 @@ public interface SupportQueries { /** Insert a request-by-size. */ @Parameter("job_id") @Parameter("num_boards") - String TEST_INSERT_REQ_SIZE = - "INSERT INTO job_request(job_id, num_boards) VALUES (?, ?)"; + String TEST_INSERT_REQ_SIZE = """ + INSERT INTO job_request( + job_id, num_boards) + VALUES (?, ?) + """; /** Insert a request-by-dimensions. */ @Parameter("job_id") @Parameter("width") @Parameter("height") @Parameter("max_dead_boards") - String TEST_INSERT_REQ_DIMS = - "INSERT INTO job_request(job_id, width, height, " - + "max_dead_boards) VALUES (?, ?, ?, ?)"; + String TEST_INSERT_REQ_DIMS = """ + INSERT INTO job_request( + job_id, width, height, max_dead_boards) + VALUES (?, ?, ?, ?) + """; /** Insert a request-for-a-board. */ @Parameter("job_id") @Parameter("board_id") - String TEST_INSERT_REQ_BOARD = - "INSERT INTO job_request(job_id, board_id) VALUES (?, ?)"; + String TEST_INSERT_REQ_BOARD = """ + INSERT INTO job_request( + job_id, board_id) + VALUES (?, ?) + """; /** Count the jobs. */ @ResultColumn("cnt") @SingleRowResult - String TEST_COUNT_JOBS = "SELECT COUNT(*) AS cnt FROM jobs"; + String TEST_COUNT_JOBS = """ + SELECT COUNT(*) AS cnt + FROM jobs + """; /** Count the active allocation requests. */ @Parameter("job_state") @ResultColumn("cnt") @SingleRowResult - String TEST_COUNT_REQUESTS = - "SELECT COUNT(*) AS cnt FROM job_request " - + "JOIN jobs USING (job_id) WHERE job_state = :job_state"; + String TEST_COUNT_REQUESTS = """ + SELECT COUNT(*) AS cnt + FROM job_request + JOIN jobs USING (job_id) + WHERE job_state = :job_state + """; /** Count the active power change requests. */ @ResultColumn("cnt") @SingleRowResult - String TEST_COUNT_POWER_CHANGES = - "SELECT COUNT(*) AS cnt FROM pending_changes"; + String TEST_COUNT_POWER_CHANGES = """ + SELECT COUNT(*) AS cnt + FROM pending_changes + """; /** Count the active power change requests. */ @ResultColumn("cnt") @@ -78,27 +94,39 @@ public interface SupportQueries { /** Directly set the state of a job. */ @Parameter("state") @Parameter("job") - String TEST_SET_JOB_STATE = - "UPDATE jobs SET job_state = :state WHERE job_id = :job"; + String TEST_SET_JOB_STATE = """ + UPDATE jobs + SET job_state = :state + WHERE job_id = :job + """; /** Directly set when a job died. */ @Parameter("timestamp") @Parameter("job") - String TEST_SET_JOB_DEATH_TIME = - "UPDATE jobs SET death_timestamp = :timestamp WHERE job_id = :job"; + String TEST_SET_JOB_DEATH_TIME = """ + UPDATE jobs + SET death_timestamp = :timestamp + WHERE job_id = :job + """; /** Get the quota for a user on a machine. */ @Parameter("machine_id") @Parameter("user_id") @ResultColumn("quota") - String TEST_GET_QUOTA = - "SELECT quota FROM quotas WHERE user_id = ?"; + String TEST_GET_QUOTA = """ + SELECT quota + FROM quotas + WHERE user_id = ? + """; /** Set the quota for a group. */ @Parameter("quota") @Parameter("group") - String TEST_SET_QUOTA = - "UPDATE user_groups SET quota = :quota WHERE group_id = :group"; + String TEST_SET_QUOTA = """ + UPDATE user_groups + SET quota = :quota + WHERE group_id = :group + """; /** Create a machine, specifying the ID. */ @Parameter("machine_id") @@ -107,10 +135,11 @@ public interface SupportQueries { @Parameter("height") @Parameter("depth") @GeneratesID - String INSERT_MACHINE = - "INSERT INTO machines(machine_id, machine_name, " - + "width, height, depth, board_model) " - + "VALUES (?, ?, ?, ?, ?, 5)"; + String INSERT_MACHINE = """ + INSERT INTO machines( + machine_id, machine_name, width, height, depth, board_model) + VALUES (?, ?, ?, ?, ?, 5) + """; /** Create a BMP, specifying the ID. */ @Parameter("bmp_id") @@ -119,9 +148,11 @@ public interface SupportQueries { @Parameter("cabinet") @Parameter("frame") @GeneratesID - String INSERT_BMP_WITH_ID = - "INSERT INTO bmp(bmp_id, machine_id, address, " - + "cabinet, frame) VALUES (?, ?, ?, ?, ?)"; + String INSERT_BMP_WITH_ID = """ + INSERT INTO bmp( + bmp_id, machine_id, address, cabinet, frame) + VALUES (?, ?, ?, ?, ?) + """; /** Create a user, specifying the ID. */ @Parameter("user_id") @@ -129,26 +160,33 @@ public interface SupportQueries { @Parameter("trust_level") @Parameter("disabled") @GeneratesID - String INSERT_USER = "INSERT INTO user_info(user_id, user_name, " - + "trust_level, disabled, encrypted_password) " - + "VALUES (?, ?, ?, ?, '*')"; + String INSERT_USER = """ + INSERT INTO user_info( + user_id, user_name, trust_level, disabled, encrypted_password) + VALUES (?, ?, ?, ?, '*') + """; /** Create a group, specifying the ID. */ @Parameter("group_id") @Parameter("group_name") @Parameter("quota") @GeneratesID - String INSERT_GROUP = "INSERT INTO " - + "user_groups(group_id, group_name, quota, group_type) " - + "VALUES (?, ?, ?, 0)"; + String INSERT_GROUP = """ + INSERT INTO user_groups( + group_id, group_name, quota, group_type) + VALUES (?, ?, ?, 0) + """; /** Create a user/group association, specifying the ID. */ @Parameter("membership_id") @Parameter("user_id") @Parameter("group_id") @GeneratesID - String INSERT_MEMBER = "INSERT group_memberships(" - + "membership_id, user_id, group_id) VALUES (?, ?, ?)"; + String INSERT_MEMBER = """ + INSERT INTO group_memberships( + membership_id, user_id, group_id) + VALUES (?, ?, ?) + """; /** Create a board, specifying the ID. */ @Parameter("board_id") @@ -163,11 +201,12 @@ public interface SupportQueries { @Parameter("root_y") @Parameter("board_power") @GeneratesID - String INSERT_BOARD_WITH_ID = - "INSERT INTO boards(board_id, address, " - + "bmp_id, board_num, machine_id, x, y, z, " - + "root_x, root_y, board_power) " - + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + String INSERT_BOARD_WITH_ID = """ + INSERT INTO boards( + board_id, address, bmp_id, board_num, machine_id, x, y, z, + root_x, root_y, board_power) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """; /** Create a job, specifying timestamps. */ @Parameter("machine_id") @@ -182,10 +221,11 @@ public interface SupportQueries { @Parameter("keepalive_interval") @Parameter("keepalive_timestamp") @GeneratesID - String INSERT_JOB_WITH_TIMESTAMPS = - "INSERT INTO jobs(machine_id, owner, group_id, root_id, " - + "job_state, create_timestamp, allocation_timestamp, " - + "death_timestamp, allocation_size, " - + "keepalive_interval, keepalive_timestamp) " - + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + String INSERT_JOB_WITH_TIMESTAMPS = """ + INSERT INTO jobs( + machine_id, owner, group_id, root_id, job_state, + create_timestamp, allocation_timestamp, death_timestamp, + allocation_size, keepalive_interval, keepalive_timestamp) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """; } diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/TestSupport.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/TestSupport.java index 990d21c345..860c103180 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/TestSupport.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/TestSupport.java @@ -35,6 +35,7 @@ import java.time.Instant; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.function.IntConsumer; import java.util.function.ObjIntConsumer; @@ -168,25 +169,34 @@ protected void killDB() throws IOException { } } + private static void checkKey(String name, int expected, + Optional got) { + if (got.isPresent() && got.get() != expected) { + log.warn("{} created with ID {} and not {}", name, got.get(), + expected); + } + } + private static void makeMachine(Connection c, int width, int height, int depth) { try (var u = c.update(INSERT_MACHINE)) { - u.call(MACHINE, MACHINE_NAME, width, height, depth); + checkKey("Machine", MACHINE, + u.key(MACHINE, MACHINE_NAME, width, height, depth)); } try (var u = c.update(INSERT_BMP_WITH_ID)) { - u.call(BMP, MACHINE, BMP_ADDR, 1, 1); + checkKey("BMP", BMP, u.key(BMP, MACHINE, BMP_ADDR, 1, 1)); } } private static void makeUser(Connection c) { try (var u = c.update(INSERT_USER)) { - u.call(USER, USER_NAME, BASIC, true); + checkKey("User", USER, u.key(USER, USER_NAME, BASIC, true)); } try (var u = c.update(INSERT_GROUP)) { - u.call(GROUP, GROUP_NAME, INITIAL_QUOTA); + checkKey("Group", GROUP, u.key(GROUP, GROUP_NAME, INITIAL_QUOTA)); } try (var u = c.update(INSERT_MEMBER)) { - u.call(MEMBERSHIP, USER, GROUP); + checkKey("Membership", MEMBERSHIP, u.key(MEMBERSHIP, USER, GROUP)); } } @@ -204,8 +214,8 @@ private static void setupDB1(Connection c) { makeMachine(c, 1, 1, 1); // Add one board to the machine try (var u = c.update(INSERT_BOARD_WITH_ID)) { - assertEquals(1, u.call(BOARD, BOARD_ADDR, BMP, 0, MACHINE, 0, 0, 0, - 0, 0, false)); + checkKey("Board", BOARD, u.key(BOARD, BOARD_ADDR, BMP, 0, MACHINE, + 0, 0, 0, 0, 0, false)); } // A disabled permission-less user with a quota makeUser(c); @@ -226,9 +236,12 @@ private static void setupDB3(Connection c) { // Add three connected boards to the machine int b0 = BOARD, b1 = BOARD + 1, b2 = BOARD + 2; try (var u = c.update(INSERT_BOARD_WITH_ID)) { - u.call(b0, BOARD_ADDR, BMP, 0, MACHINE, 0, 0, 0, 0, 0, false); - u.call(b1, "2.2.2.3", BMP, 1, MACHINE, 0, 0, 1, 8, 4, false); - u.call(b2, "2.2.2.4", BMP, 2, MACHINE, 0, 0, 2, 4, 8, false); + checkKey("Board", b0, u.key(b0, BOARD_ADDR, BMP, 0, MACHINE, 0, 0, + 0, 0, 0, false)); + checkKey("Board", b1, u.key(b1, "2.2.2.3", BMP, 1, MACHINE, 0, 0, 1, + 8, 4, false)); + checkKey("Board", b2, u.key(b2, "2.2.2.4", BMP, 2, MACHINE, 0, 0, 2, + 4, 8, false)); } try (var u = c.update(INSERT_LINK)) { u.call(b0, 0, b1, 3, true); @@ -303,8 +316,11 @@ protected static int makeFinishedJob(Connection c, int size, int time) { */ protected static void allocateBoardToJob(Connection c, int boardId, Integer jobId) { - try (var u = c.update("UPDATE boards SET allocated_job = :job " - + "WHERE board_id = :board")) { + try (var u = c.update(""" + UPDATE boards + SET allocated_job = :job + WHERE board_id = :board + """)) { u.call(jobId, boardId); } } @@ -321,16 +337,20 @@ protected static void allocateBoardToJob(Connection c, int boardId, */ protected static void setAllocRoot(Connection c, int jobId, Integer boardId) { - try (var u = c.update("UPDATE jobs SET root_id = :board, " - + "width = 1, height = 1, depth = 1 " - + "WHERE job_id = :job")) { + try (var u = c.update(""" + UPDATE jobs + SET root_id = :board, width = 1, height = 1, depth = 1 + WHERE job_id = :job + """)) { u.call(boardId, jobId); } } protected List getReports() { return db.execute(c -> { - try (var q = c.query("SELECT reported_issue FROM board_reports")) { + try (var q = c.query(""" + SELECT reported_issue FROM board_reports + """)) { return q.call(string("reported_issue")); } }); @@ -443,7 +463,10 @@ protected void withJob(IntConsumer act) { protected void nukeJob(int jobId) { db.executeVoid(c -> { - try (var u = c.update("DELETE FROM jobs WHERE job_id = ?")) { + try (var u = c.update(""" + DELETE FROM jobs + WHERE job_id = ? + """)) { u.call(jobId); } }); diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/admin/MDefLoaderTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/admin/MDefLoaderTest.java index 474992e1d3..52bb0a016d 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/admin/MDefLoaderTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/admin/MDefLoaderTest.java @@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; import static uk.ac.manchester.spinnaker.alloc.db.Row.integer; import static uk.ac.manchester.spinnaker.alloc.db.Row.string; @@ -26,6 +27,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.parallel.Execution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; @@ -49,6 +51,7 @@ @SpringBootTest @TestInstance(PER_CLASS) @ActiveProfiles("unittest") +@Execution(SAME_THREAD) class MDefLoaderTest extends SimpleDBTestBase { @ResultColumn("c") @SingleRowResult @@ -72,6 +75,9 @@ class MDefLoaderTest extends SimpleDBTestBase { @Value("classpath:bad-board-example.json") private Resource badBoard; + @Value("classpath:bad-board-example2.json") + private Resource badBoard2; + @Test void readSingleBoardExample() throws IOException { var machines = loader.readMachineDefinitions(singleBoard.getFile()); @@ -90,7 +96,7 @@ void readSingleBoardExample() throws IOException { } @Test - void readBadBoardExample() throws IOException { + void readBadBoardExample() { var e = assertThrows(IOException.class, () -> loader.readMachineDefinitions(badBoard.getFile())); assertEquals( @@ -99,6 +105,16 @@ void readBadBoardExample() throws IOException { e.getMessage()); } + @Test + void readBadBoardExample2() { + var e = assertThrows(IOException.class, + () -> loader.readMachineDefinitions(badBoard2.getFile())); + assertEquals( + "failed to validate configuration: " + + "'1.2.3.4.5.6.not-an-ip' is a bad IPv4 address", + e.getMessage()); + } + @Test @SuppressWarnings("deprecation") void loadSingleBoardExample() throws IOException { diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/AllocatorTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/AllocatorTest.java index cd43412d4e..576649bda9 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/AllocatorTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/AllocatorTest.java @@ -18,6 +18,7 @@ import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; import static uk.ac.manchester.spinnaker.alloc.model.JobState.DESTROYED; import static uk.ac.manchester.spinnaker.alloc.model.JobState.POWER; import static uk.ac.manchester.spinnaker.alloc.model.JobState.QUEUED; @@ -30,6 +31,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -53,6 +55,7 @@ "spalloc.sqlite.lock-note-threshold=2200ms", "spalloc.sqlite.lock-warn-threshold=3s" }) +@Execution(SAME_THREAD) class AllocatorTest extends TestSupport { @Autowired @@ -382,9 +385,12 @@ public void expireReady() throws Exception { getAllocTester().destroyJob(job, "test"); c.update("DELETE FROM job_request").call(); c.update("DELETE FROM pending_changes").call(); - c.update("UPDATE boards SET allocated_job = NULL, " - + "power_on_timestamp = 0, " - + "power_off_timestamp = 0").call(); + c.update(""" + UPDATE boards + SET allocated_job = NULL, + power_on_timestamp = 0, + power_off_timestamp = 0 + """).call(); }); } } @@ -395,7 +401,7 @@ public void tombstone() throws Exception { doTransactionalTest(() -> { assumeTrue(db.isHistoricalDBAvailable()); - try (Connection histConn = db.getHistoricalConnection()) { + try (var histConn = db.getHistoricalConnection()) { int job = makeQueuedJob(1); conn.update(TEST_SET_JOB_STATE).call(DESTROYED, job); diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/FirmwareLoaderTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/FirmwareLoaderTest.java index 53c64c36c9..91d7093b9c 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/FirmwareLoaderTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/FirmwareLoaderTest.java @@ -18,6 +18,7 @@ import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; //import static uk.ac.manchester.spinnaker.alloc.allocator.Cfg.BOARD; import static uk.ac.manchester.spinnaker.alloc.model.JobState.READY; @@ -28,6 +29,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.parallel.Execution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -49,6 +51,7 @@ @TestPropertySource(properties = { "spalloc.transceiver.fpga-reload=true" // Enable reloading! }) +@Execution(SAME_THREAD) class FirmwareLoaderTest extends TestSupport { private static final int TEST_TIMEOUT = 10; @@ -145,8 +148,11 @@ private void resetDBState(Connection c, int job) throws Exception { c.update("DELETE FROM job_request").call(); c.update("DELETE FROM pending_changes").call(); // Board must be bootable now to not interfere with following tests - c.update("UPDATE boards SET allocated_job = NULL, " - + "power_on_timestamp = 0, power_off_timestamp = 0").call(); + c.update(""" + UPDATE boards + SET allocated_job = NULL, + power_on_timestamp = 0, power_off_timestamp = 0 + """).call(); }); } diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/QuotaManagerTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/QuotaManagerTest.java index 1a918bc052..7caa735567 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/QuotaManagerTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/QuotaManagerTest.java @@ -17,12 +17,14 @@ import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; import static uk.ac.manchester.spinnaker.alloc.db.Row.int64; import java.io.IOException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -34,6 +36,7 @@ @SpringBootTest @SpringJUnitWebConfig(TestSupport.Config.class) @ActiveProfiles("unittest") +@Execution(SAME_THREAD) class QuotaManagerTest extends TestSupport { /** Because the regular scheduled actions are not running. */ diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/SpallocCoreTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/SpallocCoreTest.java index 5ebd0422f8..c8461f77c5 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/SpallocCoreTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/allocator/SpallocCoreTest.java @@ -329,7 +329,7 @@ void getJobs() { assertEquals(Optional.of(USER_NAME), j.getOwner()); assertEquals(Optional.empty(), j.getUrl()); assertEquals(1, j.getBoards().size()); - assertEquals(BOARD_ADDR, j.getBoards().get(0).getAddress()); + assertEquals(BOARD_ADDR, j.getBoards().get(0).address()); }); } @@ -598,13 +598,10 @@ void reportIssue() { assertEquals(List.of(), getReports()); var j = spalloc.getJob(p, jobId).orElseThrow(); - // Messy to build as usually only done by Jackson - var r = new IssueReportRequest(); - var b = new ReportedBoard(); - b.machine = "test"; - b.address = BOARD_ADDR; - r.issue = "test"; - r.boards = List.of(b); + // A bit messy to build as usually only done by Jackson + var r = new IssueReportRequest("test", + List.of(new ReportedBoard("test", null, null, null, + null, null, null, null, BOARD_ADDR))); j.reportIssue(r, p); assertEquals(List.of("test"), getReports()); diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistCommsTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistCommsTest.java index 391c855db5..19c1295428 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistCommsTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistCommsTest.java @@ -18,6 +18,7 @@ import static java.util.concurrent.Executors.newSingleThreadExecutor; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; import java.io.IOException; import java.util.Collection; @@ -29,6 +30,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.parallel.Execution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -46,6 +48,7 @@ @SpringBootTest @SpringJUnitWebConfig(TestSupport.Config.class) @ActiveProfiles("unittest") +@Execution(SAME_THREAD) class BlacklistCommsTest extends TestSupport { /** Timeouts on individual tests, in seconds. */ @@ -114,8 +117,10 @@ private Future bmpWorker(Collection bmps, int count) var future = exec.submit(() -> { ready.fire(); // Time to allow main thread to submit the work we'll carry out - Thread.sleep(TEST_DELAY); - bmpCtrl.processRequests(TEST_DELAY, bmps); + for (int i = 0; i < count; i++) { + Thread.sleep(TEST_DELAY); + bmpCtrl.processRequests(TEST_DELAY, bmps); + } return BMP_DONE_TOKEN; }); ready.await(); diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistStoreTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistStoreTest.java index 0177cdc718..52e8732f23 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistStoreTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/BlacklistStoreTest.java @@ -17,6 +17,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; import static uk.ac.manchester.spinnaker.alloc.db.Row.stream; import static uk.ac.manchester.spinnaker.machine.Direction.EAST; import static uk.ac.manchester.spinnaker.machine.Direction.NORTH; @@ -33,6 +34,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -48,6 +50,7 @@ @SpringBootTest @SpringJUnitWebConfig(TestSupport.Config.class) @ActiveProfiles("unittest") +@Execution(SAME_THREAD) class BlacklistStoreTest extends TestSupport { private BlacklistStore.TestAPI testAPI; diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/MockTransceiver.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/MockTransceiver.java index 759f8c8559..dc8d9af110 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/MockTransceiver.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/bmp/MockTransceiver.java @@ -15,15 +15,13 @@ */ package uk.ac.manchester.spinnaker.alloc.bmp; -import static java.nio.ByteBuffer.allocate; import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.unmodifiableMap; import static org.apache.commons.io.IOUtils.readFully; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.messages.model.PowerCommand.POWER_ON; -// TODO use ByteBuffer.slice(int,int) from Java 14 onwards -import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.slice; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import static uk.ac.manchester.spinnaker.utils.UnitConstants.MEGABYTE; import java.io.IOException; @@ -35,13 +33,12 @@ import java.util.Map; import java.util.zip.CRC32; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - import org.slf4j.Logger; import com.google.errorprone.annotations.concurrent.GuardedBy; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.MemoryLocation; import uk.ac.manchester.spinnaker.machine.board.BMPBoard; import uk.ac.manchester.spinnaker.machine.board.BMPCoords; @@ -101,7 +98,7 @@ public static MockTransceiver getCurrentMock() { private MockTransceiver(String machineName, BMPConnectionData data, ValueHolder setBlacklist) { log.info("constructed dummy transceiver for {} ({} : {})", machineName, - data.ipAddress, data.boards); + data.ipAddress(), data.boards()); status = new HashMap<>(); this.setBlacklist = setBlacklist; current = this; @@ -121,7 +118,7 @@ public static void setBlacklist(String blacklist) { */ private static ByteBuffer syntheticVersionData(short versionCode) { byte zero = 0; - var b = allocate(VERSION_INFO_SIZE).order(LITTLE_ENDIAN); + var b = alloc(VERSION_INFO_SIZE); b.put(zero); b.put(zero); b.put(zero); @@ -142,7 +139,7 @@ public void power(PowerCommand powerCommand, BMPCoords bmp, Collection boards) { log.info("power({},{},{})", powerCommand, bmp, boards); for (var b : boards) { - status.put(b.board, powerCommand == POWER_ON); + status.put(b.board(), powerCommand == POWER_ON); } } @@ -186,7 +183,7 @@ public String readBoardSerialNumber(BMPCoords bmp, BMPBoard board) { private static final int MEM_SIZE = 8 * MEGABYTE; private static ByteBuffer allocateMemory() { - var buf = allocate(MEM_SIZE).order(LITTLE_ENDIAN); + var buf = alloc(MEM_SIZE); buf.position(0).limit(MEM_SIZE); return buf; } @@ -201,8 +198,8 @@ public ByteBuffer readSerialFlash(BMPCoords bmp, BMPBoard board, log.info("readSerialFlash({},{},{},{})", bmp, board, baseAddress, length); // Pad to length - var b = slice(flash, baseAddress.address, length); - if (baseAddress.address == SERIAL_FLASH_BLACKLIST_OFFSET) { + var b = flash.slice(baseAddress.address(), length).order(LITTLE_ENDIAN); + if (baseAddress.address() == SERIAL_FLASH_BLACKLIST_OFFSET) { b.put(new Blacklist(blacklistData).getRawData()); b.position(0); } @@ -213,7 +210,7 @@ public ByteBuffer readSerialFlash(BMPCoords bmp, BMPBoard board, public ByteBuffer readBMPMemory(BMPCoords bmp, BMPBoard board, MemoryLocation baseAddress, int length) { log.info("readBMPMemory({},{},{},{})", bmp, board, baseAddress, length); - return slice(memory, baseAddress.address, length); + return memory.slice(baseAddress.address(), length).order(LITTLE_ENDIAN); } @Override @@ -227,7 +224,7 @@ public void writeBMPMemory(BMPCoords bmp, BMPBoard board, MemoryLocation baseAddress, ByteBuffer data) { log.info("writeBMPMemory({},{},{}:{})", bmp, board, baseAddress, data.remaining()); - slice(memory, baseAddress.address, data.remaining()) + memory.slice(baseAddress.address(), data.remaining()) .put(data.duplicate()); } @@ -236,7 +233,7 @@ public void writeFlash(@Valid BMPCoords bmp, @Valid BMPBoard board, @NotNull MemoryLocation baseAddress, @NotNull ByteBuffer data) { log.info("writeFlash({},{},{},{})", bmp, board, baseAddress, data.remaining()); - slice(memory, baseAddress.address, data.remaining()) + memory.slice(baseAddress.address(), data.remaining()) .put(data.duplicate()); var blData = data.duplicate().position(BMP_FLASH_BLACKLIST_OFFSET); synchronized (setBlacklist) { @@ -253,7 +250,8 @@ public void writeSerialFlash(BMPCoords bmp, BMPBoard board, MemoryLocation baseAddress, ByteBuffer data) { log.info("writeSerialFlash({},{},{}:{})", bmp, board, baseAddress, data.remaining()); - var b = slice(flash, baseAddress.address, data.remaining()).put(data); + var b = flash.slice(baseAddress.address(), data.remaining()) + .order(LITTLE_ENDIAN).put(data); b.position(SERIAL_FLASH_BLACKLIST_OFFSET); var bl = new Blacklist(b); synchronized (setBlacklist) { @@ -271,7 +269,7 @@ public void writeSerialFlash(BMPCoords bmp, BMPBoard board, throws IOException { log.info("writeSerialFlash({},{},{},{})", bmp, board, baseAddress, size); - slice(flash, baseAddress.address, size).put(readFully(stream, size)); + flash.slice(baseAddress.address(), size).put(readFully(stream, size)); } @Override @@ -288,7 +286,7 @@ public int readSerialFlashCRC(BMPCoords bmp, BMPBoard board, log.info("readSerialFlashCRC({},{},{},{})", bmp, board, baseAddress, length); var crc = new CRC32(); - crc.update(slice(flash, baseAddress.address, length)); + crc.update(flash.slice(baseAddress.address(), length)); return (int) (crc.getValue() & CRC_MASK); } } diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/compat/JsonTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/compat/JsonTest.java index 2b125c3867..1f202b3bbe 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/compat/JsonTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/compat/JsonTest.java @@ -30,6 +30,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.spalloc.messages.BoardCoordinates; @@ -48,6 +50,8 @@ class JsonTest { JsonTest() { // Set up the mapper in the same way that ServiceConfig does mapper = JsonMapper.builder().findAndAddModules() + .addModule(new JavaTimeModule()) + .addModule(new Jdk8Module()) .disable(WRITE_DATES_AS_TIMESTAMPS) .propertyNamingStrategy(SNAKE_CASE).build(); } @@ -76,13 +80,19 @@ void testJobMachineInfo() throws IOException, JSONException { List.of(new Connection(ZERO_ZERO, "2.3.4.5")), "gorp", List.of(new BoardCoordinates(0, 1, 2))); - JSONAssert.assertEquals( - "{ 'boards': [[0,1,2]], " - + "'connections': [[[0,0],'2.3.4.5']], " - + "'width': 0, " - + "'height': 0, " - + "'machine_name': 'gorp' }", - serialize(r), true); + JSONAssert.assertEquals(""" + { + 'boards': [ + [0,1,2] + ], + 'connections': [ + [[0, 0], '2.3.4.5'] + ], + 'width': 0, + 'height': 0, + 'machine_name': 'gorp' + } + """, serialize(r), true); } @Test @@ -95,14 +105,16 @@ void testJobState() throws IOException, JSONException { r.setKeepalive(321); r.setKeepalivehost("127.0.0.1"); - JSONAssert.assertEquals( - "{ 'state': 2, " - + "'start_time': 123, " - + "'power': false, " - + "'reason': 'gorp', " - + "'keepalive': 321, " - + "'keepalivehost': '127.0.0.1' }", - serialize(r.build()), true); + JSONAssert.assertEquals(""" + { + 'state': 2, + 'start_time': 123, + 'power': false, + 'reason': 'gorp', + 'keepalive': 321, + 'keepalivehost': '127.0.0.1' + } + """, serialize(r.build()), true); } @Test @@ -119,21 +131,24 @@ void testJobDescription() throws IOException, JSONException { r.setStartTime(321.); r.setState(State.POWER); - JSONAssert.assertEquals( - "[{ 'allocated_machine_name': 'foo', " - + "'args': [0], " - + "'boards': [], " - + "'job_id': 1, " - + "'keepalive': 123, " - + "'keepalivehost': '127.0.0.1', " - + "'kwargs': {}, " - + "'owner': 'bar', " - + "'power': false, " - + "'reason': null, " - + "'state': 2, " - + "'start_time': 321 " - + "}]", - serialize(new JobDescription[] {r.build()}), true); + JSONAssert.assertEquals(""" + [ + { + 'allocated_machine_name': 'foo', + 'args': [0], + 'boards': [], + 'job_id': 1, + 'keepalive': 123, + 'keepalivehost': '127.0.0.1', + 'kwargs': {}, + 'owner': 'bar', + 'power': false, + 'reason': null, + 'state': 2, + 'start_time': 321 + } + ] + """, serialize(new JobDescription[] {r.build()}), true); } @Test @@ -141,11 +156,18 @@ void testMachine() throws IOException, JSONException { var r = new Machine("gorp", List.of("foo", "bar"), 0, 0, null, null); - JSONAssert.assertEquals( - "[{ 'name': 'gorp', 'tags': ['foo', 'bar'], " - + "'dead_boards': [], 'dead_links': [], " - + "'height': 0, 'width': 0 }]", - serialize(new Machine[] {r}), true); + JSONAssert.assertEquals(""" + [ + { + 'name': 'gorp', + 'tags': ['foo', 'bar'], + 'dead_boards': [], + 'dead_links': [], + 'height': 0, + 'width': 0 + } + ] + """, serialize(new Machine[] {r}), true); } @Test @@ -155,12 +177,17 @@ void testWhereIs() throws IOException, JSONException { "gorp", new ChipLocation(0, 0), new BoardPhysicalCoordinates(0, 1, 2)); - JSONAssert.assertEquals( - "{'chip': [0,0], 'board_chip': [0,0]," - + "'job_chip': [0,0], 'job_id': 0," - + "'logical': [0,1,2], 'physical': [0,1,2]," - + "'machine': 'gorp'}", - serialize(r), true); + JSONAssert.assertEquals(""" + { + 'chip': [0, 0], + 'board_chip': [0, 0], + 'job_chip': [0, 0], + 'job_id': 0, + 'logical': [0, 1, 2], + 'physical': [0, 1, 2], + 'machine': 'gorp' + } + """, serialize(r), true); } } } diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatTest.java index 6d0d3ae39c..22a6c3f909 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/compat/V1CompatTest.java @@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; import static java.lang.String.format; import static java.time.temporal.ChronoUnit.MILLIS; import static java.util.regex.Pattern.compile; @@ -43,6 +44,8 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.parallel.Execution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -61,7 +64,10 @@ "spalloc.compat.service-user=" + TestSupport.USER_NAME, "spalloc.compat.service-group=" + TestSupport.GROUP_NAME }) +@Execution(SAME_THREAD) class V1CompatTest extends TestSupport { + // Take care in this class: JSON documents must be SINGLE LINE too + /** The name of the database file. */ static final String DB = "target/compat_test.sqlite3"; @@ -72,6 +78,7 @@ class V1CompatTest extends TestSupport { @BeforeEach @SuppressWarnings("deprecation") + @Timeout(1) void checkSetup(@Autowired V1CompatService compat) throws IOException { assumeTrue(db != null, "spring-configured DB engine absent"); killDB(); @@ -157,22 +164,38 @@ private static String create(PrintWriter to, UncheckedLineReader from, private static void destroy(PrintWriter to, UncheckedLineReader from, Object jobId) { + destroy(to, from, jobId, VOID_RESPONSE); + } + + private static void destroy(PrintWriter to, UncheckedLineReader from, + Object jobId, String expecting) { to.println("{\"command\":\"destroy_job\",\"args\":[" + jobId + "],\"kwargs\":{\"reason\":\"whatever\"}}"); - assertEquals(VOID_RESPONSE, from.readLine()); + assertEquals(expecting, from.readLine()); } // The actual tests + /** Big enough to stress test some internal pools. */ + private static final int MACHINERY_TEST_SIZE = 50; + + private static final int MACHINERY_TEST_TIMEOUT = 15; + @Test - public void testMachineryTest() throws Exception { - for (int i = 0; i < 100; i++) { + @Timeout(MACHINERY_TEST_TIMEOUT) + public void testMachineryTestBidirectional() throws Exception { + for (int i = 0; i < MACHINERY_TEST_SIZE; i++) { withInstance((to, from) -> { to.println(); from.readLine(); }); } - for (int i = 0; i < 100; i++) { + } + + @Test + @Timeout(MACHINERY_TEST_TIMEOUT) + public void testMachineryTestUnidirectional() throws Exception { + for (int i = 0; i < MACHINERY_TEST_SIZE; i++) { withInstance((to, from) -> { to.println(); }); @@ -183,6 +206,7 @@ public void testMachineryTest() throws Exception { @Nested class WithoutJob { @Test + @Timeout(5) void version() throws Exception { var response = "{\"return\":\"" + VERSION + "\"}"; withInstance((to, from) -> { @@ -192,6 +216,7 @@ void version() throws Exception { } @Test + @Timeout(5) void listMachines() throws Exception { var machinesResponse = "{\"return\":[{\"name\":\"" + MACHINE_NAME + "\",\"tags\":[],\"width\":1,\"height\":1," @@ -211,6 +236,7 @@ void listMachines() throws Exception { } @Test + @Timeout(5) void listJobs() throws Exception { var jobsResponse = "{\"return\":[]}"; withInstance((to, from) -> { @@ -220,6 +246,7 @@ void listJobs() throws Exception { } @Test + @Timeout(5) void notifyJob() throws Exception { withInstance((to, from) -> { to.println("{\"command\": \"notify_job\"}"); @@ -230,6 +257,7 @@ void notifyJob() throws Exception { } @Test + @Timeout(5) void notifyMachine() throws Exception { withInstance((to, from) -> { to.println("{\"command\": \"notify_machine\"}"); @@ -246,6 +274,7 @@ void notifyMachine() throws Exception { } @Test + @Timeout(5) void whereIs() throws Exception { withInstance((to, from) -> { to.println("{\"command\": \"where_is\", \"kwargs\":{" @@ -264,6 +293,7 @@ void whereIs() throws Exception { } @Test + @Timeout(5) void getBoardAtPosition() throws Exception { // Physical->Logical map var response = "{\"return\":[0,0,0]}"; @@ -277,6 +307,7 @@ void getBoardAtPosition() throws Exception { } @Test + @Timeout(5) void getBoardPosition() throws Exception { // Logical->Physical map var response = "{\"return\":[1,1,0]}"; @@ -289,20 +320,53 @@ void getBoardPosition() throws Exception { } @Test - void jobCreateDelete() throws Exception { + @Timeout(5) + void jobCreateDeleteZeroArgs() throws Exception { withInstance((to, from) -> { var jobId = create(to, from); + log.debug("created() with ID={}", jobId); destroy(to, from, jobId); + log.debug("destroyed() ID={}", jobId); + }); + } - jobId = create(to, from, 1); + @Test + @Timeout(5) + void jobCreateDeleteOneArg() throws Exception { + withInstance((to, from) -> { + var jobId = create(to, from, 1); + log.debug("created(1) with ID={}", jobId); destroy(to, from, jobId); + log.debug("destroyed(1) ID={}", jobId); + }); + } - jobId = create(to, from, 1, 1); + @Test + @Timeout(5) + void jobCreateDeleteTwoArgs() throws Exception { + withInstance((to, from) -> { + var jobId = create(to, from, 1, 1); + log.debug("created(1,1) with ID={}", jobId); destroy(to, from, jobId); + log.debug("destroyed(1,1) ID={}", jobId); + }); + } - jobId = create(to, from, 0, 0, 0); + @Test + @Timeout(5) + void jobCreateDeleteThreeArgs() throws Exception { + withInstance((to, from) -> { + var jobId = create(to, from, 0, 0, 0); + log.debug("created(0,0,0) with ID={}", jobId); destroy(to, from, jobId); + log.debug("destroyed(0,0,0) ID={}", jobId); + }); + } + @Test + @Timeout(5) + void jobCreateDeleteFourArgs() throws Exception { + withInstance((to, from) -> { to.println("{\"command\":\"create_job\",\"args\":[0,0,0,0]," + "\"kwargs\":{\"owner\":\"gorp\"," + "\"machine\":\"" + MACHINE_NAME + "\"}}"); @@ -310,10 +374,12 @@ void jobCreateDelete() throws Exception { "{\"exception\":" + "\"unsupported number of arguments: 4\"}", from.readLine()); + destroy(to, from, 999999999, "{\"exception\":\"no such job\"}"); }); } @Test + @Timeout(5) void compound() throws Exception { withInstance((to, from) -> { var jobId = create(to, from, 1, 1); @@ -345,6 +411,7 @@ class WithJob { // Make an allocated job for us to work with @BeforeEach + @Timeout(5) void setupJob() { jobId = makeJob(); expectedNotification = "{\"jobs_changed\":[" + jobId + "]}"; @@ -356,6 +423,7 @@ void setupJob() { // Get rid of the allocated job @AfterEach + @Timeout(5) void teardownJob() { db.executeVoid(c -> { allocateBoardToJob(c, BOARD, null); @@ -374,6 +442,7 @@ private String readReply(UncheckedLineReader from) { } @Test + @Timeout(5) void getJobState() throws Exception { withInstance((to, from) -> { to.println("{\"command\":\"get_job_state\",\"args\":[" + jobId @@ -386,6 +455,7 @@ void getJobState() throws Exception { } @Test + @Timeout(5) void getJobMachineInfo() throws Exception { withInstance((to, from) -> { to.println("{\"command\":\"get_job_machine_info\",\"args\":[" @@ -397,6 +467,7 @@ void getJobMachineInfo() throws Exception { } @Test + @Timeout(5) void jobKeepalive() throws Exception { withInstance((to, from) -> { to.println("{\"command\":\"job_keepalive\",\"args\":[" + jobId @@ -406,6 +477,7 @@ void jobKeepalive() throws Exception { } @Test + @Timeout(5) void jobNotify() throws Exception { withInstance((to, from) -> { to.println("{\"command\":\"notify_job\",\"args\":[" + jobId @@ -418,6 +490,7 @@ void jobNotify() throws Exception { } @Test + @Timeout(5) void jobPower() throws Exception { withInstance((to, from) -> { to.println("{\"command\":\"power_off_job_boards\"," @@ -427,6 +500,7 @@ void jobPower() throws Exception { } @Test + @Timeout(5) void whereIs() throws Exception { withInstance((to, from) -> { to.println("{\"command\":\"where_is\",\"kwargs\":{\"job_id\":" diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DMLTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DMLTest.java index c2977a5168..26ad8709c2 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DMLTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DMLTest.java @@ -271,7 +271,7 @@ void killJobAllocTask() { @Test void issueChangeForJob() { assumeWritable(c); - try (var u = c.update(issueChangeForJob)) { + try (var u = c.update(ISSUE_CHANGE_FOR_JOB)) { c.transaction(() -> { assertEquals( List.of("job_id", "board_id", "from_state", "to_state", diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DQLTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DQLTest.java index 2e97948df2..a9b8f48cd5 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DQLTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DQLTest.java @@ -254,8 +254,8 @@ void getJobChipDimensions() { var dims = q.call1(r -> new MachineDimensions(r.getInt("width"), r.getInt("height")), NO_JOB).orElseThrow(); // These two are actually NULL when there's no job - assertEquals(0, dims.width); - assertEquals(0, dims.height); + assertEquals(0, dims.width()); + assertEquals(0, dims.height()); }); } } @@ -412,7 +412,7 @@ void getAllBoardsOfAllMachines() { @Test void getDeadLinks() { - try (var q = c.query(getDeadLinks)) { + try (var q = c.query(GET_DEAD_LINKS)) { c.transaction(() -> { assertEquals(List.of("machine_id"), q.getParameters()); assertEquals(List.of("board_1_x", "board_1_y", "board_1_z", @@ -486,7 +486,7 @@ void getRootCoords() { @Test void getAllocationTasks() { - try (var q = c.query(getAllocationTasks)) { + try (var q = c.query(GET_ALLOCATION_TASKS)) { c.transaction(() -> { assertEquals(List.of("job_state"), q.getParameters()); assertEquals(List.of("req_id", "job_id", "num_boards", "width", @@ -590,7 +590,7 @@ void findRectangleAt() { @Test void findLocation() { - try (var q = c.query(findLocation)) { + try (var q = c.query(FIND_LOCATION)) { c.transaction(() -> { assertEquals(List.of("machine_id", "board_id"), q.getParameters()); @@ -1093,7 +1093,7 @@ void getLocalUserDetails() { @Test void getReportedBoards() { - try (var q = c.query(getReportedBoards)) { + try (var q = c.query(GET_REPORTED_BOARDS)) { c.transaction(() -> { assertEquals(List.of("threshold"), q.getParameters()); assertEquals(List.of("board_id", "num_reports", "x", "y", "z", diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DbBasicTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DbBasicTest.java index e46a0787ca..ded0c14684 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DbBasicTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/db/DbBasicTest.java @@ -32,8 +32,6 @@ import org.springframework.jdbc.UncategorizedSQLException; import org.springframework.test.context.ActiveProfiles; -import uk.ac.manchester.spinnaker.alloc.db.DatabaseAPI.Connection; - /** * Test that the database engine interface works and that the queries are * synchronised with the schema. Deliberately does not do meaningful testing of @@ -133,14 +131,15 @@ void testBadUpdates() { } @Test - @SuppressWarnings("deprecation") void testDbChanges() { assumeWritable(c); c.transaction(() -> { int rows; - ((Connection) c).update( - "CREATE TEMPORARY TABLE foo " - + "(k INT PRIMARY KEY AUTO_INCREMENT, x INT)").call(); + c.update(""" + CREATE TEMPORARY TABLE foo ( + k INT PRIMARY KEY AUTO_INCREMENT, + x INT) + """).call(); try (var u = c.update("INSERT INTO foo(x) VALUES(?)"); var q = c.query("SELECT x FROM foo WHERE ? = ?"); var q2 = c.query("SELECT x FROM foo")) { diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthTest.java index aab3e7288f..a52a3f47c7 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/security/LocalAuthTest.java @@ -18,11 +18,13 @@ import static java.time.Instant.now; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; import java.io.IOException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @@ -35,6 +37,7 @@ @SpringBootTest @SpringJUnitWebConfig(TestSupport.Config.class) @ActiveProfiles("unittest") +@Execution(SAME_THREAD) class LocalAuthTest extends TestSupport { private TestAPI authEngine; @@ -56,10 +59,12 @@ public void unlockUser() throws Exception { try (var c = db.getConnection()) { c.transaction(() -> { // 90k seconds is more than one day - try (var setLocked = - c.update("UPDATE user_info SET locked = :locked, " - + "last_fail_timestamp = :time - 90000 " - + "WHERE user_id = :user_id")) { + try (var setLocked = c.update(""" + UPDATE user_info + SET locked = :locked, + last_fail_timestamp = :time - 90000 + WHERE user_id = :user_id + """)) { setLocked.call(true, now(), USER); } }); @@ -69,8 +74,11 @@ public void unlockUser() throws Exception { try (var c = db.getConnection()) { assertEquals(false, c.transaction(() -> { - try (var q = c.query("SELECT locked FROM user_info " - + "WHERE user_id = :user_id")) { + try (var q = c.query(""" + SELECT locked + FROM user_info + WHERE user_id = :user_id + """)) { return q.call1(Row.bool("locked"), USER).orElseThrow(); } })); diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/JsonTest.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/JsonTest.java index 0dfe7a82cd..18198b807d 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/JsonTest.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/JsonTest.java @@ -17,9 +17,7 @@ import static com.fasterxml.jackson.databind.PropertyNamingStrategies.KEBAB_CASE; import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.*; import static uk.ac.manchester.spinnaker.alloc.model.PowerState.ON; import static uk.ac.manchester.spinnaker.machine.ChipLocation.ZERO_ZERO; @@ -31,9 +29,6 @@ import java.util.Optional; import java.util.Set; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; - import org.apache.cxf.jaxrs.impl.UriBuilderImpl; import org.json.JSONException; import org.junit.jupiter.api.Nested; @@ -42,7 +37,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.BoardLocation; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.Job; import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.Machine; @@ -62,6 +61,8 @@ class JsonTest { JsonTest() { // Set up the mapper in the same way that ServiceConfig does mapper = JsonMapper.builder().findAndAddModules() + .addModule(new JavaTimeModule()) + .addModule(new Jdk8Module()) .disable(WRITE_DATES_AS_TIMESTAMPS) .propertyNamingStrategy(KEBAB_CASE).build(); } @@ -234,11 +235,16 @@ class Serialization { @Test void testServiceDescription() throws IOException, JSONException { var d = new ServiceDescription(); - d.setVersion(new Version("1.2.3")); - JSONAssert.assertEquals( - "{ \"version\": { \"major-version\": 1," - + "\"minor-version\": 2, \"revision\": 3 } }", - serialize(d), false); + d.setVersion(Version.parse("1.2.3")); + JSONAssert.assertEquals(""" + { + "version": { + "major-version": 1, + "minor-version": 2, + "revision": 3 + } + } + """, serialize(d), false); } @Test @@ -248,23 +254,33 @@ void testStateResponse() throws IOException, JSONException { r.setStartTime(Instant.ofEpochSecond(1633954394)); r.setKeepaliveHost("127.0.0.1"); r.setOwner("gorp"); - JSONAssert.assertEquals( - "{ \"state\": \"READY\", " - + "\"start-time\": \"2021-10-11T12:13:14Z\", " - + "\"owner\": \"gorp\", " - + "\"keepalive-host\": \"127.0.0.1\" }", - serialize(r), false); + JSONAssert.assertEquals(""" + { + "state": "READY", + "start-time": "2021-10-11T12:13:14Z", + "owner": "gorp", + "keepalive-host": "127.0.0.1" + } + """, serialize(r), false); } @Test void testSubMachineResponse() throws IOException, JSONException { var r = new SubMachineResponse(new SM(), null); - JSONAssert.assertEquals( - "{ \"depth\": 1, \"width\": 1, \"height\": 1," - + "\"machine-name\": \"gorp\"," - + "\"boards\": [[0, 0, 0]]," - + "\"connections\": [[[0, 0], \"2.3.4.5\"]] }", - serialize(r), true); + JSONAssert.assertEquals(""" + { + "depth": 1, + "width": 1, + "height": 1, + "machine-name": "gorp", + "boards": [ + [0, 0, 0] + ], + "connections": [ + [[0, 0], "2.3.4.5"] + ] + } + """, serialize(r), true); } @Test @@ -317,13 +333,17 @@ public Optional getRootChip() { } }; var r = new WhereIsResponse(loc, stubBuilder("http://localhost/")); - JSONAssert.assertEquals( - "{ \"machine\": \"gorp\", \"chip\": [1, 2], " - + "\"job-id\": 12345, \"board-chip\": [3, 4]," - + "\"job-chip\": [11, 12]," - + "\"logical-board-coordinates\": [5, 6, 0]," - + "\"physical-board-coordinates\": [7, 8, 9] }", - serialize(r), false); + JSONAssert.assertEquals(""" + { + "machine": "gorp", + "chip": [1, 2], + "job-id": 12345, + "board-chip": [3, 4], + "job-chip": [11, 12], + "logical-board-coordinates": [5, 6, 0], + "physical-board-coordinates": [7, 8, 9] + } + """, serialize(r), false); } } @@ -331,41 +351,76 @@ public Optional getRootChip() { class Deserialization { @Test void testCreateJobRequestSimple() throws IOException { - var obj = "{\"owner\":\"bob\", \"keepalive-interval\":\"PT30S\"}"; - var cjr = deserialize(obj, CreateJobRequest.class); + var cjr = deserialize(""" + { + "owner": "bob", + "keepalive-interval": "PT30S" + } + """, CreateJobRequest.class); assertNotNull(cjr); - assertEquals("bob", cjr.owner); - assertNotNull(cjr.keepaliveInterval); - assertEquals(30, cjr.keepaliveInterval.getSeconds()); - assertNull(cjr.dimensions); + assertEquals("bob", cjr.owner()); + assertNotNull(cjr.keepaliveInterval()); + assertEquals(30, cjr.keepaliveInterval().getSeconds()); + assertNull(cjr.dimensions()); } @Test void testCreateJobRequestComplex() throws IOException { - var obj = "{\"owner\": \"bob\", \"keepalive-interval\": \"PT30S\", " - + "\"dimensions\": {\"width\": 1, \"height\": 2}, " - + "\"tags\": [\"a\", \"b\"], " - + "\"max-dead-boards\": 77, " - + "\"machine-name\": \"gorp\"}"; - var cjr = deserialize(obj, CreateJobRequest.class); + var cjr = deserialize(""" + { + "owner": "bob", + "keepalive-interval": "PT30S", + "dimensions": { + "width": 1, + "height": 2 + }, + "tags": ["a", "b"], + "max-dead-boards": 77, + "machine-name": "gorp" + } + """, CreateJobRequest.class); assertNotNull(cjr); - assertEquals("bob", cjr.owner); - assertNotNull(cjr.keepaliveInterval); - assertEquals(30, cjr.keepaliveInterval.getSeconds()); - assertNotNull(cjr.dimensions); - assertEquals(1, cjr.dimensions.width); - assertNotNull(cjr.tags); - assertEquals(2, cjr.tags.size()); - assertEquals("a", cjr.tags.get(0)); - assertEquals("gorp", cjr.machineName); - assertEquals(77, cjr.maxDeadBoards); + assertEquals("bob", cjr.owner()); + assertNotNull(cjr.keepaliveInterval()); + assertEquals(30, cjr.keepaliveInterval().getSeconds()); + assertNotNull(cjr.dimensions()); + assertEquals(1, cjr.dimensions().width()); + assertEquals(List.of("a", "b"), cjr.tags()); + assertEquals("gorp", cjr.machineName()); + assertEquals(77, cjr.maxDeadBoards()); + } + + @Test + void testCreateJobRequestSpecific() throws IOException { + // The part that testCreateJobRequestComplex() doesn't look at + assertEquals(3, deserialize(""" + { + "owner": "bob", + "keepalive-interval": "PT30S", + "dimensions": { + "width": 1, + "height": 2 + }, + "board": { + "cabinet": 3, + "frame": 4, + "board": 5 + }, + "tags": ["a", "b"], + "max-dead-boards": 77, + "machine-name": "gorp" + } + """, CreateJobRequest.class).board().cabinet()); } @Test void testPowerRequest() throws IOException { - var obj = "{\"power\": \"ON\"}"; - var mp = deserialize(obj, MachinePower.class); - assertEquals(ON, mp.getPower()); + var mp = deserialize(""" + { + "power": "ON" + } + """, MachinePower.class); + assertEquals(ON, mp.power()); } } } diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubJob.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubJob.java index 5fc52b4dfa..8b8e460896 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubJob.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubJob.java @@ -141,11 +141,7 @@ public void forgetProxy(ProxyCore proxy) { @Override public final boolean equals(Object other) { - if (other instanceof Job) { - var j = (Job) other; - return getId() == j.getId(); - } - return false; + return (other instanceof Job j) && (getId() == j.getId()); } @Override diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubMachine.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubMachine.java index cd63581c88..4512c11366 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubMachine.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubMachine.java @@ -129,11 +129,7 @@ public List getBoardNumbers(BMPCoords bmp) { @Override public final boolean equals(Object other) { - if (other instanceof Machine) { - var m = (Machine) other; - return getId() == m.getId(); - } - return false; + return (other instanceof Machine m) && (getId() == m.getId()); } @Override diff --git a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubUriInfo.java b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubUriInfo.java index 219a2b5e21..ed51d248ce 100644 --- a/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubUriInfo.java +++ b/SpiNNaker-allocserv/src/test/java/uk/ac/manchester/spinnaker/alloc/web/StubUriInfo.java @@ -18,10 +18,10 @@ import java.net.URI; import java.util.List; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.PathSegment; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.PathSegment; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; abstract class StubUriInfo implements UriInfo { @Override diff --git a/SpiNNaker-allocserv/src/test/resources/bad-board-example2.json b/SpiNNaker-allocserv/src/test/resources/bad-board-example2.json new file mode 100644 index 0000000000..9029aefbc8 --- /dev/null +++ b/SpiNNaker-allocserv/src/test/resources/bad-board-example2.json @@ -0,0 +1,36 @@ +{ + "machines" : [ { + "name" : "my-board", + "tags" : [ "single" ], + "width" : 1, + "height" : 1, + "dead-boards" : [ { + "x" : 0, + "y" : 0, + "z" : 1 + }, { + "x" : 0, + "y" : 0, + "z" : 2 + } ], + "dead-links" : {}, + "board-locations" : { + "[x:0,y:0,z:0]" : { + "c" : 0, + "f" : 0, + "b" : 0 + } + }, + "bmp-ips" : { + "[c:0,f:0]" : "1.2.3.4.5.6.not-an-ip" + }, + "spinnaker-ips" : { + "[x:0,y:0,z:0]" : "192.168.0.3" + } + } ], + "port" : 22244, + "ip" : "192.168.0.2", + "timeout-check-interval" : 5.0, + "max-retired-jobs" : 1200, + "seconds-before-free" : 30 +} diff --git a/SpiNNaker-comms/pom.xml b/SpiNNaker-comms/pom.xml index 24f18e65dc..8034c4fb57 100644 --- a/SpiNNaker-comms/pom.xml +++ b/SpiNNaker-comms/pom.xml @@ -53,7 +53,7 @@ limitations under the License. com.fasterxml.jackson.module - jackson-module-jaxb-annotations + jackson-module-jakarta-xmlbind-annotations org.apache.commons diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/AllocatedMachine.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/AllocatedMachine.java index e27e80ec70..6d9feda1b5 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/AllocatedMachine.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/AllocatedMachine.java @@ -21,9 +21,6 @@ import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -33,6 +30,8 @@ import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.google.errorprone.annotations.Immutable; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import uk.ac.manchester.spinnaker.alloc.client.SpallocClient.Machine; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.board.ValidTriadDepth; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/BoardCoords.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/BoardCoords.java index 6b6209c494..170aee762c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/BoardCoords.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/BoardCoords.java @@ -17,11 +17,12 @@ import static java.lang.String.format; -import java.util.Objects; - +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.errorprone.annotations.Immutable; +import uk.ac.manchester.spinnaker.machine.board.PhysicalCoords; +import uk.ac.manchester.spinnaker.machine.board.TriadCoords; import uk.ac.manchester.spinnaker.machine.board.ValidBoardNumber; import uk.ac.manchester.spinnaker.machine.board.ValidCabinetNumber; import uk.ac.manchester.spinnaker.machine.board.ValidFrameNumber; @@ -30,154 +31,65 @@ import uk.ac.manchester.spinnaker.machine.board.ValidTriadZ; import uk.ac.manchester.spinnaker.utils.validation.IPAddress; -/** Generalised coordinates of a board. */ +/** + * Generalised coordinates of a board. + * + * @param x + * Logical triad X coordinate. Range: 0–255. + * @param y + * Logical triad Y coordinate. Range: 0–255. + * @param z + * Logical triad Z coordinate. Range: 0–2. + * @param cabinet + * Number of the cabinet containing the frame containing the board. + * Range: 0–31. + * @param frame + * Number of the frame (within the cabinet) containing the board. + * Range: 0–31. + * @param board + * Number of the board within its frame. Range: 0–23. May be + * {@code null} under some circumstances. + * @param address + * IP address of ethernet chip on board. May be {@code null} if the + * current user doesn't have permission to see the board address at + * this point (because it isn't allocated or booted). + */ @Immutable -public class BoardCoords { - /** Logical triad X coordinate. */ - @ValidTriadX - private final int x; - - /** Logical triad Y coordinate. */ - @ValidTriadY - private final int y; - - /** Logical triad Z coordinate. */ - @ValidTriadZ - private final int z; - - /** Physical cabinet number. */ - @ValidCabinetNumber - private final int cabinet; - - /** Physical frame number. */ - @ValidFrameNumber - private final int frame; - - /** Physical board number. */ - @ValidBoardNumber - private final Integer board; - - /** - * IP address of ethernet chip. May be {@code null} if the current user - * doesn't have permission to see the board address at this point. - */ - @IPAddress(nullOK = true) - private final String address; - +public record BoardCoords(// + @JsonProperty("x") @ValidTriadX int x, + @JsonProperty("y") @ValidTriadY int y, + @JsonProperty("z") @ValidTriadZ int z, + @JsonProperty("cabinet") @ValidCabinetNumber int cabinet, + @JsonProperty("frame") @ValidFrameNumber int frame, + @JsonProperty("board") @ValidBoardNumber Integer board, + @JsonProperty("address") @IPAddress(nullOK = true) String address) { /** - * @param x - * Logical triad X coordinate - * @param y - * Logical triad Y coordinate - * @param z - * Logical triad Z coordinate - * @param cabinet - * Physical cabinet number - * @param frame - * Physical frame number - * @param board - * Physical board number - * @param address - * IP address of ethernet chip - */ - BoardCoords(@JsonProperty("x") int x, @JsonProperty("y") int y, - @JsonProperty("z") int z, @JsonProperty("cabinet") int cabinet, - @JsonProperty("frame") int frame, - @JsonProperty("board") Integer board, - @JsonProperty("address") String address) { - this.x = x; - this.y = y; - this.z = z; - this.cabinet = cabinet; - this.frame = frame; - this.board = board; - this.address = address; - } - - /** - * Get the triad X coordinate. Range: 0-255. - * - * @return Logical triad X coordinate. - */ - public int getX() { - return x; - } - - /** - * Get the triad Y coordinate. Range: 0-255. + * The triad coordinate triple of the board. * - * @return Logical triad Y coordinate. + * @return Logical triad coordinates. */ - public int getY() { - return y; + @JsonIgnore + public TriadCoords triad() { + return new TriadCoords(x, y, z); } /** - * Get the triad Z coordinate. Range: 0-2. + * The physical coordinate triple of the board. * - * @return Logical triad Z coordinate. + * @return Physical board coordinates, or {@code null} if the {@link #board} + * is null. */ - public int getZ() { - return z; - } - - /** - * Get the number of the cabinet containing the frame containing the board. - * - * @return Physical cabinet number. - */ - public int getCabinet() { - return cabinet; - } - - /** - * Get the number of the frame (within the cabinet) containing the board. - * - * @return Physical frame number. - */ - public int getFrame() { - return frame; - } - - /** - * Get the number of the board within its frame. - * - * @return Physical board number. - */ - public int getBoard() { - return board; - } - - /** - * Get the IP address of the Ethernet chip of the board, if available. - * - * @return IP address of ethernet chip. May be {@code null} if the - * current user doesn't have permission to see the board address - * at this point. - */ - public String getAddress() { - return address; - } - - @Override - public String toString() { - return format("Board(%d,%d,%d|%d:%d:%d|%s)", x, y, z, - cabinet, frame, board, address); - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof BoardCoords)) { - return false; + @JsonIgnore + public PhysicalCoords physicalCoords() { + if (board == null) { + return null; } - var o = (BoardCoords) other; - return x == o.x && y == o.y && z == o.z && cabinet == o.cabinet - && frame == o.frame && Objects.equals(board, o.board) - && Objects.equals(address, o.address); + return new PhysicalCoords(cabinet, frame, board); } @Override - public int hashCode() { - return (x << 16) | (y << 8) | z; + public String toString() { + return format("Board(%d,%d,%d|%d:%d:%d|%s)", x, y, z, cabinet, frame, + board, address); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/BriefMachineDescription.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/BriefMachineDescription.java index 0870de0038..f1b7781226 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/BriefMachineDescription.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/BriefMachineDescription.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.annotation.JsonAlias; -class BriefMachineDescription { +final class BriefMachineDescription { /** The machine name. */ String name; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/ClientSession.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/ClientSession.java index 93dff352b9..7bd8b72130 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/ClientSession.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/ClientSession.java @@ -241,7 +241,12 @@ public void onWebsocketPong(WebSocket conn, Framedata f) { } private synchronized int issueCorrelationId() { - return correlationCounter++; + int i = correlationCounter++; + if (i < 1) { + correlationCounter = 1; + i = 0; + } + return i; } /** @@ -308,24 +313,30 @@ public void onMessage(ByteBuffer message) { message.order(LITTLE_ENDIAN); int code = message.getInt(); switch (ProxyProtocol.values()[code]) { - case OPEN: - case CLOSE: - case OPEN_U: + case OPEN, CLOSE, OPEN_U -> + // Response to call requireNonNull(replyHandlers.remove(message.getInt()), "uncorrelated response").complete(message); - break; - case MSG: + case MSG -> + // Async message from board requireNonNull(channels.get(message.getInt()), "unrecognised channel").receive(message); - break; - case ERR: - requireNonNull(replyHandlers.remove(message.getInt()), - "uncorrelated response") - .completeExceptionally(manufactureException(message)); - break; - // case MSG_TO: // Never sent - default: - log.error("unexpected message code: {}", code); + case ERR -> { + // Error from call + int correlationId = message.getInt(); + var exception = manufactureException(message); + if (correlationId < 0) { + // General error message + log.error("general failure reported by service", exception); + } else { + // Response to a particular call + requireNonNull(replyHandlers.remove(correlationId), + "uncorrelated response") + .completeExceptionally(exception); + } + } + // case MSG_TO -> // Never sent by service, only by us + default -> log.error("unexpected message code: {}", code); } } @@ -419,9 +430,7 @@ private abstract class ChannelBase implements AutoCloseable { * The message off the websocket. */ private void receive(ByteBuffer msg) { - msg = msg.slice(); - msg.order(LITTLE_ENDIAN); - receiveQueue.add(msg); + receiveQueue.add(msg.slice().order(LITTLE_ENDIAN)); } /** @@ -624,7 +633,7 @@ public synchronized boolean trackCookie(HttpURLConnection conn) { var headerFields = conn.getHeaderFields(); var cookiesHeader = headerFields.get(SET_COOKIE); if (cookiesHeader != null) { - for (String setCookie : cookiesHeader) { + for (var setCookie : cookiesHeader) { log.debug("Cookie header: {}", setCookie); var m = SESSION_ID_RE.matcher(setCookie); if (m.find()) { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/CreateJob.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/CreateJob.java index 97e097620b..f797462af7 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/CreateJob.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/CreateJob.java @@ -21,14 +21,13 @@ import java.time.Duration; import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.google.errorprone.annotations.Keep; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.machine.board.PhysicalCoords; import uk.ac.manchester.spinnaker.machine.board.TriadCoords; import uk.ac.manchester.spinnaker.machine.board.ValidBoardNumber; @@ -327,7 +326,7 @@ public CreateJob(int width, int height) { * coordinates. */ public CreateJob(String machine, TriadCoords triad) { - board = new SpecificBoard(true, triad.x, triad.y, triad.z); + board = new SpecificBoard(true, triad.x(), triad.y(), triad.z()); machineName = machine; } @@ -343,7 +342,8 @@ public CreateJob(String machine, TriadCoords triad) { * The physical coordinates of the board to request. */ public CreateJob(String machine, PhysicalCoords coords) { - this.board = new SpecificBoard(false, coords.c, coords.f, coords.b); + this.board = + new SpecificBoard(false, coords.c(), coords.f(), coords.b()); machineName = machine; } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Jobs.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Jobs.java index e8672ff1ea..a134dd1777 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Jobs.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Jobs.java @@ -20,7 +20,7 @@ import java.net.URI; import java.util.List; -class Jobs { +final class Jobs { /** The jobs of the machine. */ List jobs; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Machines.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Machines.java index 781ca15c9f..b962cfe195 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Machines.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Machines.java @@ -19,7 +19,7 @@ import java.util.List; -class Machines { +final class Machines { /** The machine info. */ List machines; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Power.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Power.java index 06d1be9527..a24c3b43b9 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Power.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/Power.java @@ -15,7 +15,7 @@ */ package uk.ac.manchester.spinnaker.alloc.client; -class Power { +final class Power { /** The power state. */ String power; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/ProxyProtocol.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/ProxyProtocol.java index 4d05657a81..1aa976e606 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/ProxyProtocol.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/ProxyProtocol.java @@ -15,7 +15,7 @@ */ package uk.ac.manchester.spinnaker.alloc.client; -import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.nio.ByteBuffer; @@ -51,7 +51,7 @@ enum ProxyProtocol { * first word. */ ByteBuffer allocate() { - var b = ByteBuffer.allocate(size).order(LITTLE_ENDIAN); + var b = alloc(size); b.putInt(ordinal()); return b; } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/RootInfo.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/RootInfo.java index 9a2d95a67b..149f9610f1 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/RootInfo.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/RootInfo.java @@ -21,7 +21,7 @@ import uk.ac.manchester.spinnaker.messages.model.Version; -class RootInfo { +final class RootInfo { /** Service version. */ Version version; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/SpallocClient.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/SpallocClient.java index e62462aa48..51fe0d894d 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/SpallocClient.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/SpallocClient.java @@ -22,14 +22,14 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.Serial; import java.util.List; import java.util.stream.Stream; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - import com.google.errorprone.annotations.MustBeClosed; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.HasChipLocation; import uk.ac.manchester.spinnaker.machine.board.PhysicalCoords; import uk.ac.manchester.spinnaker.machine.board.TriadCoords; @@ -387,6 +387,7 @@ default void waitForPower() throws IOException { * Exception caused by the server sending an error. */ class SpallocException extends RuntimeException { + @Serial private static final long serialVersionUID = -1363689283367574333L; /** The HTTP response code that triggered the exception. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/SpallocClientFactory.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/SpallocClientFactory.java index 9ea7e307fc..947954bd24 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/SpallocClientFactory.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/alloc/client/SpallocClientFactory.java @@ -35,11 +35,9 @@ import static uk.ac.manchester.spinnaker.utils.InetFactory.getByNameQuietly; import static uk.ac.manchester.spinnaker.machine.ChipLocation.ZERO_ZERO; -import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.Inet4Address; @@ -52,6 +50,7 @@ import java.util.Map; import java.util.stream.Stream; +import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -147,9 +146,9 @@ public static Job getJobFromProxyInfo(ProxyInformation proxy) if (proxy == null) { return null; } - log.info("Using proxy {} for connections", proxy.spallocUrl); - return new SpallocClientFactory(URI.create(proxy.spallocUrl)) - .getJob(proxy.jobUrl, proxy.headers, proxy.cookies); + log.info("Using proxy {} for connections", proxy.spallocUrl()); + return new SpallocClientFactory(URI.create(proxy.spallocUrl())) + .getJob(proxy); } /** @@ -167,16 +166,7 @@ public static Job getJobFromProxyInfo(ProxyInformation proxy) * made into an instance of the given class. */ static T readJson(InputStream is, Class cls) throws IOException { - BufferedReader streamReader = new BufferedReader( - new InputStreamReader(is, "UTF-8")); - StringBuilder responseStrBuilder = new StringBuilder(); - - String inputStr; - while ((inputStr = streamReader.readLine()) != null) { - responseStrBuilder.append(inputStr); - } - String json = responseStrBuilder.toString(); - + var json = IOUtils.toString(is, UTF_8); try { return JSON_MAPPER.readValue(json, cls); } catch (IOException e) { @@ -301,26 +291,23 @@ public SpallocClient login(String username, String password) /** * Get direct access to a Job. * - * @param uri - * The URI of the job - * @param headers - * The headers to read authentication from. - * @param cookies - * The cookies to read authentication from. + * @param proxyInfo + * The information (URL, headers, cookies) about the job. * @return A job. * @throws IOException * If there is an error communicating with the server. */ - public Job getJob(String uri, Map headers, - Map cookies) throws IOException { - var u = URI.create(uri); - var s = new ClientSession(baseUrl, headers, cookies); + private Job getJob(ProxyInformation proxyInfo) throws IOException { + var u = URI.create(proxyInfo.jobUrl()); + var s = new ClientSession(baseUrl, proxyInfo.headers(), + proxyInfo.cookies()); var c = new ClientImpl(s, s.discoverRoot()); log.info("Connecting to job on {}", u); return c.job(u); } - private abstract static class Common { + private abstract static sealed class Common + permits ClientImpl, JobImpl, MachineImpl { private final SpallocClient client; final Session s; @@ -331,7 +318,7 @@ private abstract static class Common { } final Machine getMachine(String name) throws IOException { - Machine m = MACHINE_MAP.get(name); + var m = MACHINE_MAP.get(name); if (m == null) { client.listMachines(); m = MACHINE_MAP.get(name); @@ -373,7 +360,7 @@ private static final class ClientImpl extends Common private URI machines; - private ClientImpl(Session s, RootInfo ri) throws IOException { + private ClientImpl(Session s, RootInfo ri) { super(null, s); this.v = ri.version; this.jobs = asDir(ri.jobsURI); @@ -795,14 +782,14 @@ public void waitForChange() throws IOException { public WhereIs getBoard(TriadCoords coords) throws IOException { return whereis( bmd.uri.resolve(format("logical-board?x=%d&y=%d&z=%d", - coords.x, coords.y, coords.z))); + coords.x(), coords.y(), coords.z()))); } @Override public WhereIs getBoard(PhysicalCoords coords) throws IOException { return whereis(bmd.uri.resolve( format("physical-board?cabinet=%d&frame=%d&board=%d", - coords.c, coords.f, coords.b))); + coords.c(), coords.f(), coords.b()))); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/BMPConnection.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/BMPConnection.java index 2785fdcf45..72158cf132 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/BMPConnection.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/BMPConnection.java @@ -56,11 +56,11 @@ public class BMPConnection extends UDPConnection * If socket creation fails. */ public BMPConnection(BMPConnectionData connectionData) throws IOException { - super(null, null, connectionData.ipAddress, - (connectionData.portNumber == null ? SCP_SCAMP_PORT - : connectionData.portNumber), IPTOS_RELIABILITY); - coords = connectionData.bmp; - boards = connectionData.boards; + super(null, null, connectionData.ipAddress(), + (connectionData.portNumber() == null ? SCP_SCAMP_PORT + : connectionData.portNumber()), IPTOS_RELIABILITY); + coords = connectionData.bmp(); + boards = connectionData.boards(); } @Override @@ -105,7 +105,7 @@ public SCPResultMessage receiveSCPResponse(int timeout) @Override public SDPMessage receiveMessage(int timeout) throws IOException, InterruptedException { - return new SDPMessage(receive(timeout), true); + return new SDPMessage(receive(timeout)); } /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/EIEIOConnection.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/EIEIOConnection.java index 888eb5a866..4064571716 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/EIEIOConnection.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/EIEIOConnection.java @@ -129,8 +129,8 @@ protected void sendCommand(EIEIOCommand command, InetAddress ipAddress, protected EIEIOCommand receiveCommand() throws IOException, InterruptedException { var msg = receiveMessage(); - if (msg instanceof EIEIOCommandMessage) { - return ((EIEIOCommandMessage) msg).getHeader().command; + if (msg instanceof EIEIOCommandMessage cmd) { + return cmd.getHeader().command; } throw new IOException("unexpected data message"); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/IPAddressConnection.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/IPAddressConnection.java index d350160442..91b7236bac 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/IPAddressConnection.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/IPAddressConnection.java @@ -84,7 +84,7 @@ public final InetAddress receiveMessage() { public InetAddress receiveMessage(int timeout) { try { var packet = receiveWithAddress(timeout); - var addr = packet.getAddress(); + var addr = packet.address(); if (addr.getPort() == BOOTROM_SPINN_PORT) { return addr.getAddress(); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/NotificationConnection.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/NotificationConnection.java index 98655f2874..ee53ce0770 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/NotificationConnection.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/NotificationConnection.java @@ -15,15 +15,12 @@ */ package uk.ac.manchester.spinnaker.connections; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.connections.UDPConnection.TrafficClass.IPTOS_RELIABILITY; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.io.IOException; import java.net.InetAddress; -import java.nio.ByteBuffer; -import uk.ac.manchester.spinnaker.messages.notification.AbstractNotificationMessage; import uk.ac.manchester.spinnaker.messages.notification.NotificationMessage; /** @@ -94,14 +91,6 @@ public NotificationConnection(InetAddress localHost, Integer localPort, setReceivePacketSize(NOTIFICATION_MESSAGE_BUFFER_SIZE); } - /** - * @return Get a new little-endian buffer sized suitably for notification - * messages. - */ - private static ByteBuffer newMessageBuffer() { - return allocate(NOTIFICATION_MESSAGE_BUFFER_SIZE).order(LITTLE_ENDIAN); - } - /** * Sends a notification message down this connection. * @@ -112,7 +101,7 @@ private static ByteBuffer newMessageBuffer() { */ public void sendNotification(NotificationMessage notificationMessage) throws IOException { - var b = newMessageBuffer(); + var b = alloc(NOTIFICATION_MESSAGE_BUFFER_SIZE); notificationMessage.addToBuffer(b); b.flip(); send(b); @@ -122,6 +111,6 @@ public void sendNotification(NotificationMessage notificationMessage) public NotificationMessage receiveMessage(int timeout) throws IOException, InterruptedException { var b = receive(timeout); - return AbstractNotificationMessage.build(b); + return NotificationMessage.build(b); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/SDPConnection.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/SDPConnection.java index 00917766f3..ce2b285af4 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/SDPConnection.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/SDPConnection.java @@ -111,7 +111,7 @@ public SDPMessage receiveMessage(int timeout) throws IOException, InterruptedIOException, InterruptedException { var buffer = receive(timeout); buffer.getShort(); // SKIP TWO PADDING BYTES - return new SDPMessage(buffer, false); + return new SDPMessage(buffer); } /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/UDPConnection.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/UDPConnection.java index 3840c1b6ac..031a66b281 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/UDPConnection.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/UDPConnection.java @@ -17,9 +17,7 @@ import static java.lang.String.format; import static java.net.InetAddress.getByAddress; -import static java.nio.ByteBuffer.allocate; import static java.nio.ByteBuffer.wrap; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.util.Objects.requireNonNullElse; import static java.util.stream.Collectors.joining; import static java.util.stream.IntStream.range; @@ -28,6 +26,7 @@ import static uk.ac.manchester.spinnaker.messages.Constants.SCP_SCAMP_PORT; import static uk.ac.manchester.spinnaker.messages.sdp.SDPHeader.Flag.REPLY_NOT_EXPECTED; import static uk.ac.manchester.spinnaker.messages.sdp.SDPPort.RUNNING_COMMAND_SDP_PORT; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import static uk.ac.manchester.spinnaker.utils.MathUtils.hexbyte; import static uk.ac.manchester.spinnaker.utils.Ping.ping; @@ -390,7 +389,7 @@ public final ByteBuffer receive(int timeout) protected ByteBuffer doReceive(int timeout) throws SocketTimeoutException, IOException, InterruptedException { socket.setSoTimeout(timeout); - var buffer = allocate(receivePacketSize); + var buffer = alloc(receivePacketSize); var pkt = new DatagramPacket(buffer.array(), receivePacketSize); socket.receive(pkt); buffer.position(pkt.getLength()).flip(); @@ -399,7 +398,7 @@ protected ByteBuffer doReceive(int timeout) pkt.getSocketAddress()); log.debug("message data: {}", describe(buffer)); } - return buffer.order(LITTLE_ENDIAN); + return buffer; } @Override @@ -430,7 +429,7 @@ public final UDPPacket receiveWithAddress(int timeout) protected UDPPacket doReceiveWithAddress(int timeout) throws SocketTimeoutException, IOException { socket.setSoTimeout(timeout); - var buffer = allocate(receivePacketSize); + var buffer = alloc(receivePacketSize); var pkt = new DatagramPacket(buffer.array(), receivePacketSize); socket.receive(pkt); buffer.position(pkt.getLength()).flip(); @@ -439,7 +438,7 @@ protected UDPPacket doReceiveWithAddress(int timeout) pkt.getSocketAddress()); log.debug("message data: {}", describe(buffer)); } - return new UDPPacket(buffer.order(LITTLE_ENDIAN), + return new UDPPacket(buffer, (InetSocketAddress) pkt.getSocketAddress()); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/UDPPacket.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/UDPPacket.java index 95c66110ee..ab5f300fcc 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/UDPPacket.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/UDPPacket.java @@ -20,38 +20,11 @@ /** * A packet with an address. + * + * @param byteBuffer + * The buffer with the content of the packet. + * @param address + * The address that the packet came from or is going to. */ -public class UDPPacket { - private final ByteBuffer byteBuffer; - - private final InetSocketAddress address; - - /** - * Create a buffer with an address. - * - * @param byteBuffer - * The buffer - * @param address - * The address - */ - UDPPacket(ByteBuffer byteBuffer, InetSocketAddress address) { - this.byteBuffer = byteBuffer; - this.address = address; - } - - /** - * Get the buffer with the content of the packet. - * @return The buffer - */ - public ByteBuffer getByteBuffer() { - return byteBuffer; - } - - /** - * Get the address. - * @return The address - */ - public InetSocketAddress getAddress() { - return address; - } +public record UDPPacket(ByteBuffer byteBuffer, InetSocketAddress address) { } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/InvalidPacketException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/InvalidPacketException.java deleted file mode 100644 index 36ace3d11e..0000000000 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/InvalidPacketException.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2018 The University of Manchester - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package uk.ac.manchester.spinnaker.connections.model; - -import java.io.IOException; - -/** - * Indicates that a packet with an unsupported format was received. - * - * @author Donal Fellows - */ -public class InvalidPacketException extends IOException { - private static final long serialVersionUID = -2509633246846245166L; - - /** - * Create an instance. - * - * @param message - * The exception message. - */ - public InvalidPacketException(String message) { - super(message); - } - - /** - * Create an instance. - * - * @param message - * The exception message. - * @param cause - * The cause of the exception. - */ - public InvalidPacketException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/MessageHandler.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/MessageHandler.java index 9331ddf030..7b121fedcb 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/MessageHandler.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/MessageHandler.java @@ -22,6 +22,7 @@ * the type of message handled. * @author Donal Fellows */ +@FunctionalInterface public interface MessageHandler { /** * The callback for handling the message. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/SCPSenderReceiver.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/SCPSenderReceiver.java index 61380f160f..9dd13f2c2d 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/SCPSenderReceiver.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/connections/model/SCPSenderReceiver.java @@ -66,12 +66,9 @@ default ByteBuffer getSCPData(SCPRequest scpRequest) { default void send(SCPRequest request) throws IOException { var msg = getSCPData(request); switch (request.sdpHeader.getFlags()) { - case REPLY_EXPECTED: - case REPLY_EXPECTED_NO_P2P: + case REPLY_EXPECTED, REPLY_EXPECTED_NO_P2P -> send(msg, request.scpRequestHeader.getSequence()); - break; - default: - send(msg); + default -> send(msg); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/Utils.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/Utils.java deleted file mode 100644 index be6330f036..0000000000 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/Utils.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2023 The University of Manchester - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package uk.ac.manchester.spinnaker.messages; - -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; -import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; - -import java.nio.ByteBuffer; - -/** - * Utilities for working with messages. - */ -public abstract class Utils { - private Utils() { - } - - /** - * Convert a word to a buffer that could form part of a message understood - * by SpiNNaker. - * - * @param value - * The value to put in the buffer as a single 32-bit word. - * @return The buffer, flipped. The buffer is writable and has a backing - * array. - */ - public static ByteBuffer wordAsBuffer(int value) { - ByteBuffer b = allocate(WORD_SIZE).order(LITTLE_ENDIAN); - b.putInt(value).flip(); - return b; - } -} diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPRequest.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPRequest.java index 4cf6b75665..5a69f447c5 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPRequest.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPRequest.java @@ -41,19 +41,24 @@ * @param * The type of the response to the request. */ -public abstract class BMPRequest - extends SCPRequest { +public abstract sealed class BMPRequest + extends SCPRequest + permits EraseFlash, GetBMPVersion, GetFPGAResetStatus, InitFPGA, + ReadADC, ReadBMPMemory, ReadCANStatus, ReadFPGARegister, ReadIPAddress, + ReadSerialFlash, ReadSerialFlashCRC, ReadSerialVector, ResetFPGA, + SetBoardLEDs, SetPower, UpdateFlash, WriteBMPMemory, WriteFlashBuffer, + WriteFPGAData, WriteFPGARegister, WriteSerialFlash { private static SDPHeader header(int board) { return new SDPHeader(REPLY_EXPECTED, new SDPLocation(0, 0, board), DEFAULT_PORT); } private static SDPHeader header(BMPBoard board) { - return header(board.board); + return header(board.board()); } private static SDPHeader header(Collection boards) { - return header(boards.stream().mapToInt(b -> b.board).min().orElse(0)); + return header(boards.stream().mapToInt(b -> b.board()).min().orElse(0)); } /** @@ -172,7 +177,7 @@ private static SDPHeader header(Collection boards) { @Override public String toString() { - StringBuilder builder = new StringBuilder(); + var builder = new StringBuilder(); builder.append(getClass().getSimpleName()); builder.append('('); builder.append("command="); @@ -197,7 +202,8 @@ public String toString() { * @see CheckOKResponse */ @UsedInJavadocOnly(CheckOKResponse.class) - public static class BMPResponse extends SCPResponse { + public static sealed class BMPResponse extends SCPResponse + permits PayloadedResponse { /** * Make a response object. * @@ -223,8 +229,14 @@ public BMPResponse(String operation, SCPCommand command, * @param * The type of the parsed payload. */ - public abstract static class PayloadedResponse extends BMPResponse - implements Supplier { + public abstract static sealed class PayloadedResponse extends BMPResponse + implements Supplier + permits EraseFlash.Response, GetBMPVersion.Response, + GetFPGAResetStatus.Response, ReadADC.Response, + ReadBMPMemory.Response, ReadCANStatus.Response, + ReadFPGARegister.Response, ReadIPAddress.Response, + ReadSerialFlash.Response, ReadSerialFlashCRC.Response, + ReadSerialVector.Response { private final T value; /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/EraseFlash.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/EraseFlash.java index 117a8bf1d2..60ae74208b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/EraseFlash.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/EraseFlash.java @@ -46,13 +46,13 @@ public final class EraseFlash extends BMPRequest { * If the baseAddress or size make no sense */ public EraseFlash(BMPBoard board, MemoryLocation baseAddress, int size) { - super(board, CMD_FLASH_ERASE, baseAddress.address, - baseAddress.address + size); + super(board, CMD_FLASH_ERASE, baseAddress.address(), + baseAddress.address() + size); // Check that we've been actually asked to do something sane! if (size <= 0) { throw new IllegalArgumentException("no data"); } - int addr = baseAddress.address; + int addr = baseAddress.address(); if (addr < 0 || addr + size > MEMORY_LIMIT || addr + size < 0) { throw new IllegalArgumentException("address not in flash"); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/GetBMPVersion.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/GetBMPVersion.java index 9b4442afa4..9071a8c8ad 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/GetBMPVersion.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/GetBMPVersion.java @@ -29,7 +29,7 @@ *

* Calls or {@code cmd_ver()} in {@code bmp_cmd.c}. */ -public class GetBMPVersion extends BMPRequest { +public final class GetBMPVersion extends BMPRequest { /** * @param board * The board to get the version from diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/GetFPGAResetStatus.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/GetFPGAResetStatus.java index 6e57a32c42..77a22ffbd3 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/GetFPGAResetStatus.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/GetFPGAResetStatus.java @@ -32,7 +32,7 @@ * Calls {@code cmd_read()} in {@code bmp_cmd.c} with special parameters and * parses the result. */ -public class GetFPGAResetStatus +public final class GetFPGAResetStatus extends BMPRequest { /** * @param board diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/InitFPGA.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/InitFPGA.java index 41e72d9491..a050c496e6 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/InitFPGA.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/InitFPGA.java @@ -28,7 +28,7 @@ *

* Calls {@code fpga_init()} in {@code bmp_hw.c}. */ -public class InitFPGA extends BMPRequest { +public final class InitFPGA extends BMPRequest { /** * @param board * Which board's FPGA to initialise. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadADC.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadADC.java index f234476aa8..b4317ad4ef 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadADC.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadADC.java @@ -39,7 +39,7 @@ * monitoring of these values is done in {@code check_status()} in * {@code bmp_main.c}. */ -public class ReadADC extends BMPRequest { +public final class ReadADC extends BMPRequest { /** * @param board * which board to request the ADC register from diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPReadMemory.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadBMPMemory.java similarity index 89% rename from SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPReadMemory.java rename to SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadBMPMemory.java index 39fefa4ee3..df5eac5e6b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPReadMemory.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadBMPMemory.java @@ -15,10 +15,10 @@ */ package uk.ac.manchester.spinnaker.messages.bmp; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.messages.model.TransferUnit.efficientTransferUnit; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_READ; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import java.nio.ByteBuffer; @@ -32,7 +32,7 @@ *

* Calls {@code cmd_read()} in {@code bmp_cmd.c}. */ -public class BMPReadMemory extends BMPRequest { +public final class ReadBMPMemory extends BMPRequest { private static int validate(int size) { if (size < 1 || size > UDP_MESSAGE_MAX_SIZE) { throw new IllegalArgumentException( @@ -49,8 +49,8 @@ private static int validate(int size) { * @param size * The number of bytes to read, between 1 and 256 */ - public BMPReadMemory(BMPBoard board, MemoryLocation address, int size) { - super(board, CMD_READ, address.address, validate(size), + public ReadBMPMemory(BMPBoard board, MemoryLocation address, int size) { + super(board, CMD_READ, address.address(), validate(size), efficientTransferUnit(address, size).value); } @@ -73,7 +73,7 @@ protected final class Response /** @return The data read, in a little-endian read-only buffer. */ @Override protected ByteBuffer parse(ByteBuffer buffer) { - return buffer.asReadOnlyBuffer().order(LITTLE_ENDIAN); + return readOnly(buffer); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadCANStatus.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadCANStatus.java index 796cde1d82..d5c925e9de 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadCANStatus.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadCANStatus.java @@ -34,7 +34,7 @@ * Handled in {@code cmd_bmp_info()} (in {@code bmp_cmd.c}) by reading from * {@code can_status}. */ -public class ReadCANStatus extends BMPRequest { +public final class ReadCANStatus extends BMPRequest { private static final int MAX_BOARDS_PER_FRAME = 24; private static final BMPBoard FRAME_ROOT = new BMPBoard(0); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadFPGARegister.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadFPGARegister.java index 35395fbc8a..1fbdf5a7d1 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadFPGARegister.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadFPGARegister.java @@ -31,7 +31,8 @@ *

* Calls {@code cmd_fpga_read()} in {@code bmp_cmd.c}. */ -public class ReadFPGARegister extends BMPRequest { +public final class ReadFPGARegister + extends BMPRequest { /** * @param fpga * FPGA (0, 1 or 2 on SpiNN-5 board) to communicate with. @@ -45,7 +46,7 @@ public class ReadFPGARegister extends BMPRequest { */ public ReadFPGARegister(FPGA fpga, MemoryLocation register, BMPBoard board) { - super(board, CMD_FPGA_READ, register.address, WORD_SIZE, fpga.value); + super(board, CMD_FPGA_READ, register.address(), WORD_SIZE, fpga.value); if (!register.isAligned()) { throw new IllegalArgumentException( "FPGA register addresses must be aligned"); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadIPAddress.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadIPAddress.java index 5eb979c88c..6e3555995b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadIPAddress.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadIPAddress.java @@ -15,6 +15,7 @@ */ package uk.ac.manchester.spinnaker.messages.bmp; +import static java.lang.System.arraycopy; import static java.net.InetAddress.getByAddress; import static uk.ac.manchester.spinnaker.messages.bmp.BMPInfo.IP_ADDR; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_BMP_INFO; @@ -34,7 +35,7 @@ *

* Handled by {@code cmd_bmp_info()} in {@code bmp_cmd.c}. */ -public class ReadIPAddress extends BMPRequest { +public final class ReadIPAddress extends BMPRequest { /** * @param board * which board to request the IP address data from @@ -48,6 +49,34 @@ public Response getSCPResponse(ByteBuffer buffer) throws Exception { return new Response(buffer); } + private static final int CHUNK_LEN = 32; + + private static final int IP_OFFSET = 8; + + private static final int IP_LEN = 4; + + /** + * Get a slice out of the result buffer and pick the IP address out of that. + * + * @param buffer + * The result buffer. Position will be updated. + * @return The parsed IP address. + * @throws UnknownHostException + * Unexpected; we are presenting the right number of bytes. + */ + private static InetAddress getIP(ByteBuffer buffer) + throws UnknownHostException { + /* + * NB: CHUNK_LEN != IP_LEN so we *must* copy like this or otherwise + * mess around with the buffer position. This is easiest. + */ + var chunk = new byte[CHUNK_LEN]; + buffer.get(chunk); + var bytes = new byte[IP_LEN]; + arraycopy(chunk, IP_OFFSET, bytes, 0, IP_LEN); + return getByAddress(bytes); + } + /** An SCP response to a request for IP address information. */ protected final class Response extends BMPRequest.PayloadedResponse { @@ -56,25 +85,11 @@ private Response(ByteBuffer buffer) super("Read IP Address Data", CMD_BMP_INFO, buffer); } - private static final int CHUNK_LEN = 32; - - private static final int IP_OFFSET = 8; - - private static final int IP_LEN = 4; - - private InetAddress getIP(ByteBuffer buffer) - throws UnknownHostException { - byte[] chunk = new byte[CHUNK_LEN]; - buffer.get(chunk); - byte[] bytes = new byte[IP_LEN]; - System.arraycopy(chunk, IP_OFFSET, bytes, 0, IP_LEN); - return getByAddress(bytes); - } - /** @return The addresses of the SpiNNaker board. */ @Override protected Addresses parse(ByteBuffer buffer) { try { + // Tricky point: order of evaluation matters return new Addresses(getIP(buffer), getIP(buffer)); } catch (UnknownHostException e) { // Should be unreachable diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialFlash.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialFlash.java index 95424657cd..7e4071049c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialFlash.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialFlash.java @@ -15,10 +15,10 @@ */ package uk.ac.manchester.spinnaker.messages.bmp; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.messages.bmp.SerialFlashOp.READ; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_BMP_SF; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import java.nio.ByteBuffer; @@ -32,7 +32,8 @@ *

* Calls {@code sf_read()} in {@code bmp_ssp.c}. */ -public class ReadSerialFlash extends BMPRequest { +public final class ReadSerialFlash + extends BMPRequest { private static int validate(int size) { if (size < 1 || size > UDP_MESSAGE_MAX_SIZE) { throw new IllegalArgumentException( @@ -50,7 +51,7 @@ private static int validate(int size) { * The number of bytes to read, between 1 and 256 */ public ReadSerialFlash(BMPBoard board, MemoryLocation address, int size) { - super(board, CMD_BMP_SF, address.address, validate(size), READ.value); + super(board, CMD_BMP_SF, address.address(), validate(size), READ.value); } @Override @@ -72,7 +73,7 @@ protected final class Response /** @return The data read, in a little-endian read-only buffer. */ @Override protected ByteBuffer parse(ByteBuffer buffer) { - return buffer.asReadOnlyBuffer().order(LITTLE_ENDIAN); + return readOnly(buffer); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialFlashCRC.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialFlashCRC.java index 21cfb51ccc..981b31e080 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialFlashCRC.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialFlashCRC.java @@ -30,7 +30,7 @@ *

* Calls {@code sf_crc32()} in {@code bmp_ssp.c}. */ -public class ReadSerialFlashCRC +public final class ReadSerialFlashCRC extends BMPRequest { /** * @param board @@ -42,7 +42,7 @@ public class ReadSerialFlashCRC */ public ReadSerialFlashCRC(BMPBoard board, MemoryLocation baseAddress, int size) { - super(board, CMD_BMP_SF, baseAddress.address, size, CRC.value); + super(board, CMD_BMP_SF, baseAddress.address(), size, CRC.value); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialVector.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialVector.java index f6e7d7bc29..1f79fb30d7 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialVector.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ReadSerialVector.java @@ -16,11 +16,15 @@ package uk.ac.manchester.spinnaker.messages.bmp; import static uk.ac.manchester.spinnaker.messages.bmp.BMPInfo.SERIAL; +import static uk.ac.manchester.spinnaker.messages.model.SerialVector.SERIAL_LENGTH; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_BMP_INFO; import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import uk.ac.manchester.spinnaker.machine.MemoryLocation; import uk.ac.manchester.spinnaker.machine.board.BMPBoard; +import uk.ac.manchester.spinnaker.messages.model.SerialVector; import uk.ac.manchester.spinnaker.messages.model.UnexpectedResponseCodeException; /** @@ -29,7 +33,8 @@ *

* Handled by {@code cmd_bmp_info()} in {@code bmp_cmd.c}. */ -public class ReadSerialVector extends BMPRequest { +public final class ReadSerialVector + extends BMPRequest { /** * @param board * which board to request the serial data from @@ -54,7 +59,16 @@ private Response(ByteBuffer buffer) /** @return The serial data. */ @Override protected SerialVector parse(ByteBuffer buffer) { - return new SerialVector(buffer); + var b = buffer.asIntBuffer(); + var hardwareVersion = b.get(); + var sn = new int[SERIAL_LENGTH]; + b.get(sn); + var serialNumber = IntBuffer.wrap(sn); + var flashBuffer = new MemoryLocation(b.get()); + var boardStat = new MemoryLocation(b.get()); + var cortexBoot = new MemoryLocation(b.get()); + return new SerialVector(hardwareVersion, serialNumber, flashBuffer, + boardStat, cortexBoot); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ResetFPGA.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ResetFPGA.java index f2fb5d61ba..33d288edd0 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ResetFPGA.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/ResetFPGA.java @@ -29,7 +29,7 @@ *

* Calls {@code fpga_reset()} in {@code bmp_hw.c}. */ -public class ResetFPGA extends BMPRequest { +public final class ResetFPGA extends BMPRequest { /** * @param board * Which board to reset the FPGAs of. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPSetLED.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SetBoardLEDs.java similarity index 90% rename from SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPSetLED.java rename to SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SetBoardLEDs.java index 4afc48d379..76e86ca839 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPSetLED.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SetBoardLEDs.java @@ -33,7 +33,7 @@ * Handled by {@code cmp_led()} in {@code bmp_cmd.c}. */ @UsedInJavadocOnly(SetLED.class) -public class BMPSetLED extends BMPRequest { +public final class SetBoardLEDs extends BMPRequest { /** * Make a request. * @@ -44,7 +44,7 @@ public class BMPSetLED extends BMPRequest { * @param boards * The boards to talk to */ - public BMPSetLED(Collection leds, LEDAction action, + public SetBoardLEDs(Collection leds, LEDAction action, Collection boards) { super(boards, CMD_LED, argument1(action, leds), argument2(boards)); } @@ -54,7 +54,7 @@ private static int argument1(LEDAction action, Collection leds) { } private static int argument2(Collection boards) { - return boards.stream().mapToInt(board -> 1 << board.board).sum(); + return boards.stream().mapToInt(board -> 1 << board.board()).sum(); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SetPower.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SetPower.java index aa6166b64e..4729fee2e1 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SetPower.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SetPower.java @@ -35,7 +35,7 @@ * Handled by {@code cmd_power()} in {@code bmp_cmd.c}, which in turn calls * {@code proc_power()} in the same file. */ -public class SetPower extends BMPRequest { +public final class SetPower extends BMPRequest { private static final int DELAY_SHIFT = 16; private static final BMPBoard FRAME_ROOT = new BMPBoard(0); @@ -61,7 +61,7 @@ private static int argument1(double delay, PowerCommand powerCommand) { } private static int boardMask(Collection boards) { - return boards.stream().mapToInt(board -> 1 << board.board).sum(); + return boards.stream().mapToInt(board -> 1 << board.board()).sum(); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/UpdateFlash.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/UpdateFlash.java index 037c788e74..7b23258ca6 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/UpdateFlash.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/UpdateFlash.java @@ -41,7 +41,7 @@ public final class UpdateFlash extends BMPRequest { * The number of bytes to copy */ public UpdateFlash(BMPBoard board, MemoryLocation baseAddress, int size) { - super(board, CMD_FLASH_COPY, REAL_FLASH_ADDRESS, baseAddress.address, + super(board, CMD_FLASH_COPY, REAL_FLASH_ADDRESS, baseAddress.address(), size); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPWriteMemory.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteBMPMemory.java similarity index 89% rename from SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPWriteMemory.java rename to SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteBMPMemory.java index da7bb2e29f..1255f105dd 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/BMPWriteMemory.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteBMPMemory.java @@ -28,7 +28,7 @@ *

* Calls {@code cmd_write()} in {@code bmp_cmd.c}. */ -public class BMPWriteMemory extends BMPRequest { +public final class WriteBMPMemory extends BMPRequest { /** * @param board * the board with the BMP to write the memory of @@ -40,9 +40,9 @@ public class BMPWriteMemory extends BMPRequest { * must extend up to the limit. The position and limit of * the buffer will not be updated by this constructor. */ - public BMPWriteMemory(BMPBoard board, MemoryLocation baseAddress, + public WriteBMPMemory(BMPBoard board, MemoryLocation baseAddress, ByteBuffer data) { - super(board, CMD_WRITE, baseAddress.address, data.remaining(), + super(board, CMD_WRITE, baseAddress.address(), data.remaining(), efficientTransferUnit(baseAddress, data.remaining()).value, data); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFPGAData.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFPGAData.java index 0af192e764..0358e62b45 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFPGAData.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFPGAData.java @@ -29,7 +29,7 @@ *

* Calls {@code ssp1_copy()} in {@code bmp_ssp.c}. */ -public class WriteFPGAData extends BMPRequest { +public final class WriteFPGAData extends BMPRequest { /** * @param board * Which board to upload the FPGA data to. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFPGARegister.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFPGARegister.java index 4d2540538b..8716424d5b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFPGARegister.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFPGARegister.java @@ -16,8 +16,8 @@ package uk.ac.manchester.spinnaker.messages.bmp; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; -import static uk.ac.manchester.spinnaker.messages.Utils.wordAsBuffer; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_FPGA_WRITE; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.wordAsBuffer; import java.nio.ByteBuffer; @@ -37,7 +37,8 @@ * @see The SpI/O project * on GitHub */ -public class WriteFPGARegister extends BMPRequest { +public final class WriteFPGARegister + extends BMPRequest { /** * @param fpga * FPGA (0, 1 or 2 on SpiNN-5 board) to communicate with. @@ -53,7 +54,7 @@ public class WriteFPGARegister extends BMPRequest { */ public WriteFPGARegister(FPGA fpga, MemoryLocation register, int value, BMPBoard board) { - super(board, CMD_FPGA_WRITE, register.address, WORD_SIZE, fpga.value, + super(board, CMD_FPGA_WRITE, register.address(), WORD_SIZE, fpga.value, wordAsBuffer(value)); if (!register.isAligned()) { throw new IllegalArgumentException( diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFlashBuffer.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFlashBuffer.java index 5f9a8df339..bcd74fc636 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFlashBuffer.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteFlashBuffer.java @@ -28,7 +28,7 @@ *

* Calls {@code cmd_flash_write()} in {@code bmp_cmd.c}. */ -public class WriteFlashBuffer extends BMPRequest { +public final class WriteFlashBuffer extends BMPRequest { /** The size of chunk that will be transferred. Fixed. */ public static final int FLASH_CHUNK_SIZE = 4096; @@ -42,7 +42,7 @@ public class WriteFlashBuffer extends BMPRequest { */ public WriteFlashBuffer(BMPBoard board, MemoryLocation baseAddress, boolean erase) { - super(board, CMD_FLASH_WRITE, baseAddress.address, FLASH_CHUNK_SIZE, + super(board, CMD_FLASH_WRITE, baseAddress.address(), FLASH_CHUNK_SIZE, erase ? 1 : 0); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteSerialFlash.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteSerialFlash.java index 1fb553f5c1..56afbbc762 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteSerialFlash.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/WriteSerialFlash.java @@ -29,7 +29,7 @@ *

* Calls {@code sf_write()} in {@code bmp_ssp.c}. */ -public class WriteSerialFlash extends BMPRequest { +public final class WriteSerialFlash extends BMPRequest { /** The size of chunk that will be transferred. Fixed. */ public static final int FLASH_CHUNK_SIZE = 256; @@ -45,7 +45,7 @@ public class WriteSerialFlash extends BMPRequest { */ public WriteSerialFlash(BMPBoard board, MemoryLocation baseAddress, ByteBuffer data) { - super(board, CMD_BMP_SF, baseAddress.address, FLASH_CHUNK_SIZE, + super(board, CMD_BMP_SF, baseAddress.address(), FLASH_CHUNK_SIZE, WRITE.value, limitSlice(data, FLASH_CHUNK_SIZE)); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootDataBlock.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootDataBlock.java index 668e4e42a2..3a4191fbf1 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootDataBlock.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootDataBlock.java @@ -27,7 +27,7 @@ * * @author Donal Fellows */ -class BootDataBlock extends BootMessage { +final class BootDataBlock extends BootMessage { private static final int BOOT_DATA_OPERAND_1 = (BOOT_MESSAGE_DATA_WORDS - 1) << NBBY; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootMessage.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootMessage.java index 53880b75cb..dae13f5599 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootMessage.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootMessage.java @@ -25,7 +25,8 @@ * A message used for booting the board. Note that boot messages are big endian, * unlike the rest of SpiNNaker. */ -public class BootMessage implements SerializableMessage { +public sealed class BootMessage implements SerializableMessage + permits BootDataBlock, EndOfBootMessages, StartOfBootMessages { private static final short BOOT_MESSAGE_VERSION = 1; private static final int BOOT_PACKET_SIZE = 256 * WORD_SIZE; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootMessages.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootMessages.java index 080a999f08..b38276abc4 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootMessages.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/BootMessages.java @@ -16,13 +16,11 @@ package uk.ac.manchester.spinnaker.messages.boot; import static java.lang.Integer.reverseBytes; -import static java.lang.Math.ceil; import static java.lang.Math.min; import static java.lang.String.format; import static java.lang.System.currentTimeMillis; import static java.nio.ByteBuffer.allocate; import static java.nio.ByteOrder.BIG_ENDIAN; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.util.stream.IntStream.range; import static java.util.stream.Stream.concat; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; @@ -30,6 +28,8 @@ import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.boot_signature; import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.is_root_chip; import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.unix_timestamp; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; +import static uk.ac.manchester.spinnaker.utils.MathUtils.ceildiv; import static uk.ac.manchester.spinnaker.utils.UnitConstants.KILOBYTE; import static uk.ac.manchester.spinnaker.utils.UnitConstants.MSEC_PER_SEC; @@ -87,14 +87,13 @@ private BootMessages(SystemVariableBootValues bootVariables, } bootData = readBootImage(getClass().getResource(BOOT_IMAGE)); injectBootVariableBlock(bootVariables); - numDataPackets = - (int) ceil(bootData.limit() / (float) BOOT_MESSAGE_DATA_BYTES); + numDataPackets = ceildiv(bootData.limit(), BOOT_MESSAGE_DATA_BYTES); } private void injectBootVariableBlock( SystemVariableBootValues bootVariables) { // NB: Endian shenanigans! - var buffer = allocate(BOOT_VARIABLE_SIZE).order(LITTLE_ENDIAN); + var buffer = alloc(BOOT_VARIABLE_SIZE); bootVariables.addToBuffer(buffer); buffer.position(0); for (int i = 0; i < BOOT_STRUCT_REPLACE_LENGTH / WORD_SIZE; i++) { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/EndOfBootMessages.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/EndOfBootMessages.java index 669fcf6782..1f0d142491 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/EndOfBootMessages.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/EndOfBootMessages.java @@ -22,7 +22,7 @@ * * @author Donal Fellows */ -class EndOfBootMessages extends BootMessage { +final class EndOfBootMessages extends BootMessage { EndOfBootMessages() { super(FLOOD_FILL_CONTROL, 1, 0, 0); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/StartOfBootMessages.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/StartOfBootMessages.java index 4ec65b886e..139d8b609e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/StartOfBootMessages.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/StartOfBootMessages.java @@ -22,7 +22,7 @@ * * @author Donal Fellows */ -class StartOfBootMessages extends BootMessage { +final class StartOfBootMessages extends BootMessage { /** * @param numPackets * The number of payload packets to be sent. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/SystemVariableBootValues.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/SystemVariableBootValues.java index 9e3e56eeb0..2731331569 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/SystemVariableBootValues.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/boot/SystemVariableBootValues.java @@ -31,7 +31,7 @@ * Default values of the system variables that get passed to SpiNNaker during * boot. */ -public class SystemVariableBootValues implements SerializableMessage { +public final class SystemVariableBootValues implements SerializableMessage { /** The size of the boot variable block, in bytes. */ static final int BOOT_VARIABLE_SIZE = 256; @@ -81,13 +81,13 @@ public void setValue(SystemVariableDefinition systemVariable, switch (systemVariable.type) { case BYTE_ARRAY: var defbytes = (byte[]) values.get(systemVariable); - if (!(value instanceof byte[])) { + if (!(value instanceof byte[] newbytes)) { throw new IllegalArgumentException("need a byte array"); } - var newbytes = (byte[]) value; if (newbytes.length != defbytes.length) { throw new IllegalArgumentException( - "byte array length must be " + defbytes.length); + "byte array length must be " + defbytes.length + + " long"); } break; case ADDRESS: diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/AbstractDataElement.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/AbstractDataElement.java index 97d19f3187..7bd1663f4c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/AbstractDataElement.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/AbstractDataElement.java @@ -20,7 +20,8 @@ /** * A marker interface for possible data elements in the EIEIO data packet. */ -public interface AbstractDataElement { +public sealed interface AbstractDataElement + permits KeyDataElement, KeyPayloadDataElement { /** * Writes this message chunk into the given buffer. This is so that a * message can be sent. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/CustomEIEIOCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/CustomEIEIOCommand.java index 26eceba051..0562813f73 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/CustomEIEIOCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/CustomEIEIOCommand.java @@ -21,7 +21,7 @@ * @see EIEIOCommandID#get(int) * @author Donal Fellows */ -public class CustomEIEIOCommand implements EIEIOCommand { +public final class CustomEIEIOCommand implements EIEIOCommand { // Must be power of 2 (minus 1) private static final int MAX_COMMAND = 0x3FFF; @@ -30,6 +30,8 @@ public class CustomEIEIOCommand implements EIEIOCommand { /** * @param command * The ID value of the command. + * @throws IllegalArgumentException + * If the command ID is negative or too large. */ CustomEIEIOCommand(int command) { if (command < 0 || command > MAX_COMMAND) { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOCommand.java index e48e5f6ee4..02a6d01b77 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOCommand.java @@ -21,7 +21,8 @@ * * @author Donal Fellows */ -public interface EIEIOCommand { +public sealed interface EIEIOCommand + permits EIEIOCommandID, CustomEIEIOCommand { /** * Get the encoded ID number of the command. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOCommandMessage.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOCommandMessage.java index dc32793451..91d54ae271 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOCommandMessage.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOCommandMessage.java @@ -27,8 +27,11 @@ * @author Sergio Davies * @author Donal Fellows */ -public class EIEIOCommandMessage - implements EIEIOMessage { +public sealed class EIEIOCommandMessage + implements EIEIOMessage + permits EventStopRequest, HostDataRead, HostDataReadAck, + HostSendSequencedData, PaddingRequest, SpinnakerRequestBuffers, + SpinnakerRequestReadData, StartRequests, StopRequests { // Must be power of 2 (minus 1) private static final int MAX_COMMAND = 0x3FFF; @@ -78,7 +81,7 @@ public Header getHeader() { } /** EIEIO header for command packets. */ - public static class Header implements EIEIOHeader { + public static final class Header implements EIEIOHeader { /** The command ID in this header. */ public final EIEIOCommand command; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIODataMessage.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIODataMessage.java index bda2a56634..6dfc4e0152 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIODataMessage.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIODataMessage.java @@ -19,10 +19,11 @@ import static java.lang.Math.floorDiv; import static java.lang.Short.toUnsignedInt; import static java.lang.String.format; -import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static java.util.Collections.emptyIterator; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.messages.eieio.EIEIOPrefix.LOWER_HALF_WORD; import static uk.ac.manchester.spinnaker.transceiver.Utils.newMessageBuffer; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import java.nio.ByteBuffer; import java.util.Iterator; @@ -36,7 +37,8 @@ * @author Sergio Davies * @author Donal Fellows */ -public class EIEIODataMessage implements EIEIOMessage, +public final class EIEIODataMessage + implements EIEIOMessage, MappableIterable { private final Header header; @@ -63,7 +65,7 @@ public EIEIODataMessage(EIEIOType eieioType) { EIEIODataMessage(ByteBuffer data) { this.header = new Header(data); this.elements = null; - this.data = data.asReadOnlyBuffer().order(LITTLE_ENDIAN); + this.data = readOnly(data); } /** @@ -166,7 +168,7 @@ public void addKey(int key) { /** @return The raw data of this message. */ public ByteBuffer getData() { - return data.asReadOnlyBuffer().order(LITTLE_ENDIAN); + return readOnly(data); } /** @@ -198,44 +200,35 @@ public void addToBuffer(ByteBuffer buffer) { @Override public Iterator iterator() { - final var d = - data == null ? null : data.duplicate().order(LITTLE_ENDIAN); + if (data == null) { + return emptyIterator(); + } + + var d = readOnly(data); return new Iterator() { private int elementsRead = 0; @Override public boolean hasNext() { - return d != null && elementsRead < header.getCount(); + return elementsRead < header.getCount(); } @Override public AbstractDataElement next() { - if (d == null || !hasNext()) { + if (!hasNext()) { throw new NoSuchElementException("read all elements"); } elementsRead++; - int key; - Integer payload; - switch (header.eieioType) { - case KEY_16_BIT: - key = d.getShort(); - payload = null; - break; - case KEY_PAYLOAD_16_BIT: - key = d.getShort(); - payload = (int) d.getShort(); - break; - case KEY_32_BIT: - key = d.getInt(); - payload = null; - break; - case KEY_PAYLOAD_32_BIT: - key = d.getInt(); - payload = d.getInt(); - break; - default: - throw new IllegalStateException(); - } + int key = switch (header.eieioType) { + case KEY_16_BIT, KEY_PAYLOAD_16_BIT -> + toUnsignedInt(d.getShort()); + case KEY_32_BIT, KEY_PAYLOAD_32_BIT -> d.getInt(); + }; + var payload = switch (header.eieioType) { + case KEY_PAYLOAD_16_BIT -> toUnsignedInt(d.getShort()); + case KEY_PAYLOAD_32_BIT -> d.getInt(); + default -> null; + }; if (header.prefix != null) { key |= header.prefix << header.prefixType.shift; } @@ -246,10 +239,9 @@ public AbstractDataElement next() { payload = header.payloadBase; } } - if (payload == null) { - return new KeyDataElement(key); - } - return new KeyPayloadDataElement(key, payload, header.isTime); + return (payload == null) ? new KeyDataElement(key) + : new KeyPayloadDataElement(key, payload, + header.isTime); } }; } @@ -260,7 +252,7 @@ public Header getHeader() { } /** EIEIO header for data packets. */ - public static class Header implements EIEIOHeader { + public static final class Header implements EIEIOHeader { /** The type of packet (size of various fields). */ public final EIEIOType eieioType; @@ -327,12 +319,9 @@ private Header(ByteBuffer buffer) { count = buffer.get(); byte flags = buffer.get(); boolean havePrefix = bit(flags, PREFIX_BIT) != 0; - if (havePrefix) { - prefixType = - EIEIOPrefix.getByValue(bit(flags, PREFIX_TYPE_BIT)); - } else { - prefixType = null; - } + prefixType = havePrefix + ? EIEIOPrefix.getByValue(bit(flags, PREFIX_TYPE_BIT)) + : null; boolean havePayload = bit(flags, PAYLOAD_BIT) != 0; isTime = bit(flags, TIME_BIT) != 0; eieioType = EIEIOType.getByValue(bits(flags, TYPE_BITS)); @@ -343,18 +332,12 @@ private Header(ByteBuffer buffer) { prefix = null; } if (havePayload) { - switch (eieioType) { - case KEY_PAYLOAD_16_BIT: - case KEY_16_BIT: - payloadBase = toUnsignedInt(buffer.getShort()); - break; - case KEY_PAYLOAD_32_BIT: - case KEY_32_BIT: - payloadBase = buffer.getInt(); - break; - default: - payloadBase = null; - } + payloadBase = switch (eieioType) { + case KEY_PAYLOAD_16_BIT, KEY_16_BIT -> + toUnsignedInt(buffer.getShort()); + case KEY_PAYLOAD_32_BIT, KEY_32_BIT -> buffer.getInt(); + default -> null; + }; } else { payloadBase = null; } @@ -442,16 +425,11 @@ public void addToBuffer(ByteBuffer buffer) { return; } switch (eieioType) { - case KEY_PAYLOAD_16_BIT: - case KEY_16_BIT: + case KEY_PAYLOAD_16_BIT, KEY_16_BIT -> buffer.putShort((short) payloadBase.intValue()); - return; - case KEY_PAYLOAD_32_BIT: - case KEY_32_BIT: + case KEY_PAYLOAD_32_BIT, KEY_32_BIT -> buffer.putInt(payloadBase.intValue()); - return; - default: - throw new IllegalStateException("unexpected EIEIO type"); + default -> throw new IllegalStateException("unexpected EIEIO type"); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOHeader.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOHeader.java index 63f2431869..bb5043427b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOHeader.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOHeader.java @@ -22,5 +22,6 @@ * * @author Donal Fellows */ -public interface EIEIOHeader extends SerializableMessage { +public sealed interface EIEIOHeader extends SerializableMessage + permits EIEIOCommandMessage.Header, EIEIODataMessage.Header { } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessage.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessage.java index c9cabe373c..9ee8ca9a4b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessage.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessage.java @@ -23,8 +23,8 @@ * @param

* The type of header on this message. */ -public interface EIEIOMessage
- extends SerializableMessage { +public sealed interface EIEIOMessage
extends + SerializableMessage permits EIEIOCommandMessage, EIEIODataMessage { /** @return the header of this message. */ Header getHeader(); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessageFactory.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessageFactory.java index cf775ab52c..9e043727f7 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessageFactory.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessageFactory.java @@ -40,37 +40,29 @@ private EIEIOMessageFactory() { */ public static EIEIOCommandMessage readCommandMessage(ByteBuffer data) { var command = peekCommand(data); - if (!(command instanceof EIEIOCommandID)) { - return new EIEIOCommandMessage(data); - } - switch ((EIEIOCommandID) command) { - case EVENT_PADDING: - // Fill in buffer area with padding - return new PaddingRequest(); - case EVENT_STOP: - // End of all buffers, stop execution - return new EventStopRequest(); - case STOP_SENDING_REQUESTS: - // Stop complaining that there is SDRAM free space for buffers - return new StopRequests(); - case START_SENDING_REQUESTS: - // Start complaining that there is SDRAM free space for buffers - return new StartRequests(); - case SPINNAKER_REQUEST_BUFFERS: - // SpiNNaker requesting new buffers for spike source population - return new SpinnakerRequestBuffers(data); - case HOST_SEND_SEQUENCED_DATA: - // Buffers being sent from host to SpiNNaker - return new HostSendSequencedData(data); - case SPINNAKER_REQUEST_READ_DATA: - // Buffers available to be read from a buffered out vertex - return new SpinnakerRequestReadData(data); - case HOST_DATA_READ: - // Host confirming data being read form SpiNNaker memory - return new HostDataRead(data); - default: + if (!(command instanceof EIEIOCommandID cmd)) { return new EIEIOCommandMessage(data); } + return switch (cmd) { + // Fill in buffer area with padding + case EVENT_PADDING -> new PaddingRequest(); + // End of all buffers, stop execution + case EVENT_STOP -> new EventStopRequest(); + // Stop complaining that there is SDRAM free space for buffers + case STOP_SENDING_REQUESTS -> new StopRequests(); + // Start complaining that there is SDRAM free space for buffers + case START_SENDING_REQUESTS -> new StartRequests(); + // SpiNNaker requesting new buffers for spike source population + case SPINNAKER_REQUEST_BUFFERS -> new SpinnakerRequestBuffers(data); + // Buffers being sent from host to SpiNNaker + case HOST_SEND_SEQUENCED_DATA -> new HostSendSequencedData(data); + // Buffers available to be read from a buffered out vertex + case SPINNAKER_REQUEST_READ_DATA -> new SpinnakerRequestReadData(data); + // Host confirming data being read form SpiNNaker memory + case HOST_DATA_READ -> new HostDataRead(data); + // Some non-standard message + default -> new EIEIOCommandMessage(data); + }; } /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessageHandler.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessageHandler.java index 269b0ac7a7..a6216bb2dc 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessageHandler.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOMessageHandler.java @@ -26,10 +26,10 @@ public interface EIEIOMessageHandler extends MessageHandler> { @Override default void handle(EIEIOMessage message) { - if (message instanceof EIEIOCommandMessage) { - handleCommand((EIEIOCommandMessage) message); - } else if (message instanceof EIEIODataMessage) { - handleData((EIEIODataMessage) message); + if (message instanceof EIEIOCommandMessage cmd) { + handleCommand(cmd); + } else if (message instanceof EIEIODataMessage data) { + handleData(data); } else { throw new IllegalArgumentException( "unsupported message type: " + message.getClass()); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOPrefix.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOPrefix.java index 957032e937..86eec0e5ff 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOPrefix.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EIEIOPrefix.java @@ -53,7 +53,7 @@ public int getValue() { * if the encoded prefix encoding is unrecognised. */ public static EIEIOPrefix getByValue(int value) { - for (EIEIOPrefix p : values()) { + for (var p : values()) { if (p.value == value) { return p; } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EventStopRequest.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EventStopRequest.java index e20b1f0cd9..7933d7c6b8 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EventStopRequest.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/EventStopRequest.java @@ -21,7 +21,7 @@ * Packet used for the buffering input technique which causes the parser of the * input packet to terminate its execution. */ -public class EventStopRequest extends EIEIOCommandMessage { +public final class EventStopRequest extends EIEIOCommandMessage { /** Make a request to stop sending events. */ public EventStopRequest() { super(EVENT_STOP); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostDataRead.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostDataRead.java index afa68bf83f..327ce9179a 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostDataRead.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostDataRead.java @@ -28,7 +28,7 @@ * * @see HostDataReadAck */ -public class HostDataRead extends EIEIOCommandMessage { +public final class HostDataRead extends EIEIOCommandMessage { private final Header header; private final Ack acks; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostDataReadAck.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostDataReadAck.java index 9ab2a14d6f..5b6e75ea46 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostDataReadAck.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostDataReadAck.java @@ -26,7 +26,7 @@ * * @see HostDataRead */ -public class HostDataReadAck extends EIEIOCommandMessage { +public final class HostDataReadAck extends EIEIOCommandMessage { /** The message sequence number that is being acknowledged. */ public final byte sequenceNumber; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostSendSequencedData.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostSendSequencedData.java index 308fb499e2..ba26e46a88 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostSendSequencedData.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/HostSendSequencedData.java @@ -26,7 +26,7 @@ * input mechanism to identify packet which needs to be stored in memory for * future use. */ -public class HostSendSequencedData extends EIEIOCommandMessage { +public final class HostSendSequencedData extends EIEIOCommandMessage { /** What region will be moved. */ public final int regionID; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/KeyDataElement.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/KeyDataElement.java index 77b66a8b90..ecd19e3943 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/KeyDataElement.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/KeyDataElement.java @@ -18,7 +18,7 @@ import java.nio.ByteBuffer; /** A data element that contains just a key. */ -public class KeyDataElement implements AbstractDataElement { +public final class KeyDataElement implements AbstractDataElement { private final int key; /** @@ -32,21 +32,16 @@ public KeyDataElement(int key) { } @Override - public final void addToBuffer(ByteBuffer buffer, EIEIOType eieioType) { + public void addToBuffer(ByteBuffer buffer, EIEIOType eieioType) { if (eieioType.payloadBytes != 0) { throw new IllegalArgumentException( "The type specifies a payload, but this element has no" + " payload"); } switch (eieioType) { - case KEY_16_BIT: - buffer.putShort((short) key); - return; - case KEY_32_BIT: - buffer.putInt(key); - return; - default: - throw new IllegalArgumentException("Unknown type"); + case KEY_16_BIT -> buffer.putShort((short) key); + case KEY_32_BIT -> buffer.putInt(key); + default -> throw new IllegalArgumentException("Unknown type"); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/KeyPayloadDataElement.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/KeyPayloadDataElement.java index 2fe5bfc076..9b46a9b256 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/KeyPayloadDataElement.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/KeyPayloadDataElement.java @@ -18,7 +18,7 @@ import java.nio.ByteBuffer; /** A data element that contains a key and a payload. */ -public class KeyPayloadDataElement implements AbstractDataElement { +public final class KeyPayloadDataElement implements AbstractDataElement { private final int key; private final int payload; @@ -47,23 +47,22 @@ public boolean isTimestamp() { } @Override - public final void addToBuffer(ByteBuffer buffer, EIEIOType eieioType) { + public void addToBuffer(ByteBuffer buffer, EIEIOType eieioType) { if (eieioType.payloadBytes == 0) { throw new IllegalArgumentException( "The type specifies no payload, but this element has a" + " payload"); } switch (eieioType) { - case KEY_PAYLOAD_16_BIT: + case KEY_PAYLOAD_16_BIT -> { buffer.putShort((short) key); buffer.putShort((short) payload); - return; - case KEY_PAYLOAD_32_BIT: + } + case KEY_PAYLOAD_32_BIT -> { buffer.putInt(key); buffer.putInt(payload); - return; - default: - throw new IllegalArgumentException("Unknown type"); + } + default -> throw new IllegalArgumentException("Unknown type"); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/PaddingRequest.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/PaddingRequest.java index eaba6f7714..29e0ecd0e8 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/PaddingRequest.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/PaddingRequest.java @@ -20,7 +20,7 @@ /** * Packet used to pad space in the buffering area, if needed. */ -public class PaddingRequest extends EIEIOCommandMessage { +public final class PaddingRequest extends EIEIOCommandMessage { /** Make an instance. */ public PaddingRequest() { super(EVENT_PADDING); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/SpinnakerRequestBuffers.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/SpinnakerRequestBuffers.java index 8b9ccef25e..e5da55e79a 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/SpinnakerRequestBuffers.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/SpinnakerRequestBuffers.java @@ -28,7 +28,7 @@ * the SpiNNaker system to the host computer to ask for more data to inject * during the simulation. */ -public class SpinnakerRequestBuffers extends EIEIOCommandMessage +public final class SpinnakerRequestBuffers extends EIEIOCommandMessage implements HasCoreLocation { /** What core are we talking about. */ public final HasCoreLocation core; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/SpinnakerRequestReadData.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/SpinnakerRequestReadData.java index 654033a89e..c856dd6f37 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/SpinnakerRequestReadData.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/SpinnakerRequestReadData.java @@ -28,7 +28,7 @@ * from the SpiNNaker system to the host computer to signal that some data is * available to be read. */ -public class SpinnakerRequestReadData extends EIEIOCommandMessage +public final class SpinnakerRequestReadData extends EIEIOCommandMessage implements HasCoreLocation { private final int numRequests; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/StartRequests.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/StartRequests.java index 4796384056..2a7ca30f82 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/StartRequests.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/StartRequests.java @@ -22,7 +22,7 @@ * to the SpiNNaker system that, if needed, it is possible to send more * "SpinnakerRequestBuffers" packet. */ -public class StartRequests extends EIEIOCommandMessage { +public final class StartRequests extends EIEIOCommandMessage { /** Make an instance. */ public StartRequests() { super(START_SENDING_REQUESTS); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/StopRequests.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/StopRequests.java index 7f959140c1..43611eb762 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/StopRequests.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/eieio/StopRequests.java @@ -22,7 +22,7 @@ * to the SpiNNaker system that to stop sending "SpinnakerRequestBuffers" * packet. */ -public class StopRequests extends EIEIOCommandMessage { +public final class StopRequests extends EIEIOCommandMessage { /** Make an instance. */ public StopRequests() { super(STOP_SENDING_REQUESTS); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ADCInfo.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ADCInfo.java index f469e7a1fc..d238f56a4c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ADCInfo.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ADCInfo.java @@ -22,12 +22,14 @@ import static uk.ac.manchester.spinnaker.messages.Constants.BMP_V_SCALE_2_5; import static uk.ac.manchester.spinnaker.messages.Constants.BMP_V_SCALE_3_3; +import java.io.Serial; import java.io.Serializable; import java.nio.ByteBuffer; /** Container for the board status data thats been retrieved from a BMP. */ @SARKStruct(value = "board_stat_t", api = SARKStruct.API.BMP) public final class ADCInfo implements Serializable { + @Serial private static final long serialVersionUID = 8245655123474028462L; /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Addresses.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Addresses.java index f813de95f9..ddb3b0f7b9 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Addresses.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Addresses.java @@ -17,23 +17,14 @@ import java.net.InetAddress; -/** The IP addresses associated with a SpiNNaker board. */ -public final class Addresses { - // TODO convert to record in 17 - /** The IPv4 address of the BMP. */ - public final InetAddress bmpIPAddress; - - /** The IPv4 address of the managed SpiNNaker board. */ - public final InetAddress spinIPAddress; - - /** - * @param bmpIPAddress - * The IPv4 address of the BMP. - * @param spinIPAddress - * The IPv4 address of the managed SpiNNaker board. - */ - public Addresses(InetAddress bmpIPAddress, InetAddress spinIPAddress) { - this.bmpIPAddress = bmpIPAddress; - this.spinIPAddress = spinIPAddress; - } +/** + * The IP addresses associated with a SpiNNaker board. + * + * @param bmpIPAddress + * The IPv4 address of the BMP. + * @param spinIPAddress + * The IPv4 address of the managed SpiNNaker board. + * @author Donal Fellows + */ +public record Addresses(InetAddress bmpIPAddress, InetAddress spinIPAddress) { } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/AppID.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/AppID.java index 83edfd79c5..d699b27e43 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/AppID.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/AppID.java @@ -20,8 +20,10 @@ * owners. * * @author Donal Fellows + * @param appID + * The proposed ID of the application. Must be between 0 and 255. */ -public final class AppID { +public record AppID(int appID) { /** * Maximum app ID. */ @@ -32,39 +34,12 @@ public final class AppID { */ public static final AppID DEFAULT = new AppID(0); - /** - * The application ID. - */ - public final int appID; - - /** - * Create an application ID. - * - * @param appID - * The proposed ID of the application. Must be between 0 and 255. - * @throws IllegalArgumentException - * If an illegal ID is given. - */ - public AppID(int appID) { + /** Make an instance. */ + public AppID { if (appID < 0 || appID > MAX_APP_ID) { throw new IllegalArgumentException( "appID must be between 0 and " + MAX_APP_ID); } - this.appID = appID; - } - - @Override - public boolean equals(Object o) { - if (o instanceof AppID) { - var other = (AppID) o; - return appID == other.appID; - } - return false; - } - - @Override - public int hashCode() { - return (appID << 5) ^ 1236984681; } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/BMPConnectionData.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/BMPConnectionData.java index 49c53cad3f..4d619cbb27 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/BMPConnectionData.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/BMPConnectionData.java @@ -26,8 +26,7 @@ import java.util.Collection; import java.util.List; -import javax.validation.Valid; - +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.board.BMPBoard; import uk.ac.manchester.spinnaker.machine.board.BMPCoords; import uk.ac.manchester.spinnaker.utils.validation.IPAddress; @@ -36,26 +35,20 @@ /** * Contains the details of a connection to a SpiNNaker Board Management * Processor (BMP). + * + * @param boards + * The boards to be addressed. + * @param bmp + * The coordinates of the BMP that manages a set of boards. + * @param ipAddress + * The IP address of the BMP. + * @param portNumber + * The port number associated with the BMP connection, or + * {@code null} for the default. */ -public class BMPConnectionData { - /** The boards to be addressed. Unmodifiable. */ - public final Collection<@Valid BMPBoard> boards; - - /** The coordinates of the BMP that manages a set of boards. */ - @Valid - public final BMPCoords bmp; - - /** The IP address of the BMP. */ - @IPAddress - public final InetAddress ipAddress; - - /** - * The port number associated with the BMP connection, or {@code null} for - * the default. - */ - @UDPPort - public final Integer portNumber; - +public record BMPConnectionData(Collection<@Valid BMPBoard> boards, + @Valid BMPCoords bmp, @IPAddress InetAddress ipAddress, + @UDPPort Integer portNumber) { /** * @param cabinet * The number of the cabinet containing the frame. @@ -70,11 +63,8 @@ public class BMPConnectionData { */ public BMPConnectionData(int cabinet, int frame, InetAddress ipAddress, Collection boards, Integer portNumber) { - this.bmp = new BMPCoords(cabinet, frame); - this.ipAddress = ipAddress; - this.boards = boards.stream().map(BMPBoard::new) - .collect(toUnmodifiableList()); - this.portNumber = portNumber; + this(boards.stream().map(BMPBoard::new).collect(toUnmodifiableList()), + new BMPCoords(cabinet, frame), ipAddress, portNumber); } /** @@ -89,15 +79,10 @@ public BMPConnectionData(int cabinet, int frame, InetAddress ipAddress, */ public BMPConnectionData(BMPCoords coords, InetAddress ipAddress, Collection boards, Integer portNumber) { - this.bmp = coords; - this.ipAddress = ipAddress; - this.boards = boards.stream().map(BMPBoard::new) - .collect(toUnmodifiableList()); - this.portNumber = portNumber; + this(boards.stream().map(BMPBoard::new).collect(toUnmodifiableList()), + coords, ipAddress, portNumber); } - private static final int MIN_BYTE_FIELD = 3; - /** * Work out the BMP connection IP address given the machine details. This is * assumed to be the IP address of the machine, with 1 subtracted from the @@ -121,6 +106,18 @@ public BMPConnectionData(BMPCoords coords, InetAddress ipAddress, */ public BMPConnectionData(InetAddress host, int numBoards) throws UnknownHostException { + /* + * Assumes a single board (or small group) with no cabinet or frame + * specified + */ + this(makeBoards(numBoards), new BMPCoords(0, 0), guessBMPIP(host), + SCP_SCAMP_PORT); + } + + private static final int MIN_BYTE_FIELD = 3; + + private static InetAddress guessBMPIP(InetAddress host) + throws UnknownHostException { // take the IP address, split by dots, and subtract 1 off last bit var ipBits = host.getAddress(); if (ipBits[MIN_BYTE_FIELD] == 0 || ipBits[MIN_BYTE_FIELD] == 1) { @@ -129,19 +126,16 @@ public BMPConnectionData(InetAddress host, int numBoards) "BMP address would have illegal IP address"); } ipBits[MIN_BYTE_FIELD]--; - ipAddress = getByAddress(ipBits); - portNumber = SCP_SCAMP_PORT; - - // Assume a single board with no cabinet or frame specified - bmp = new BMPCoords(0, 0); + return getByAddress(ipBits); + } - // add board scope for each split - // if null, the end user didn't enter anything, so assume one board - // starting at position 0 - if (numBoards == 0) { - boards = List.of(new BMPBoard(0)); + private static List makeBoards(int numBoards) { + // if 0 or crazy, the end user didn't enter anything useful, so + // assume one board starting at position 0 + if (numBoards < 1) { + return List.of(new BMPBoard(0)); } else { - boards = range(0, numBoards).mapToObj(BMPBoard::new) + return range(0, numBoards).mapToObj(BMPBoard::new) .collect(toUnmodifiableList()); } } @@ -149,9 +143,9 @@ public BMPConnectionData(InetAddress host, int numBoards) @Override public String toString() { return format( - "(c:%d,f:%d,b:%s; %s)", bmp.cabinet, bmp.frame, + "(c:%d,f:%d,b:%s; %s)", bmp.cabinet(), bmp.frame(), boards.stream().findFirst() - .map(board -> board.board + "...").orElse(""), + .map(board -> board.board() + "...").orElse(""), ipAddress); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Blacklist.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Blacklist.java index d353af1bb0..b888dcc986 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Blacklist.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Blacklist.java @@ -16,7 +16,6 @@ package uk.ac.manchester.spinnaker.messages.model; import static java.lang.Integer.parseInt; -import static java.nio.ByteBuffer.allocate; import static java.nio.ByteBuffer.wrap; import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.nio.charset.StandardCharsets.UTF_8; @@ -27,6 +26,7 @@ import static java.util.regex.Pattern.compile; import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; +import static java.util.stream.Collectors.toUnmodifiableSet; import static java.util.stream.IntStream.range; import static org.apache.commons.io.IOUtils.buffer; import static org.slf4j.LoggerFactory.getLogger; @@ -37,6 +37,8 @@ import static uk.ac.manchester.spinnaker.machine.MachineDefaults.SIZE_Y_OF_ONE_BOARD; import static uk.ac.manchester.spinnaker.machine.SpiNNakerTriadGeometry.getSpinn5Geometry; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import static uk.ac.manchester.spinnaker.utils.CollectionUtils.OR; import static uk.ac.manchester.spinnaker.utils.CollectionUtils.toEnumSet; @@ -45,6 +47,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.Serial; import java.io.Serializable; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -61,15 +64,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.validation.Valid; - import org.slf4j.Logger; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.Direction; import uk.ac.manchester.spinnaker.machine.SpiNNakerTriadGeometry; import uk.ac.manchester.spinnaker.machine.ValidP; -import uk.ac.manchester.spinnaker.utils.CollectionUtils; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; /** @@ -81,6 +82,7 @@ * @author Donal Fellows */ public final class Blacklist implements Serializable { + @Serial private static final long serialVersionUID = -7759940789892168209L; private static final Logger log = getLogger(Blacklist.class); @@ -162,8 +164,7 @@ public Blacklist(Set deadChips, } private ByteBuffer encodeBlacklist() { - var buf = allocate((SPINN5_CHIPS_PER_BOARD + 1) * WORD_SIZE) - .order(LITTLE_ENDIAN); + var buf = alloc((SPINN5_CHIPS_PER_BOARD + 1) * WORD_SIZE); buf.putInt(0); // Size; filled in later int count = 0; for (int x = 0; x < SIZE_X_OF_ONE_BOARD; x++) { @@ -308,13 +309,14 @@ private static String deleteMatched(Matcher m) { return sb.toString(); } - private static Set parseCommaSeparatedSet(String str) { - return CollectionUtils.parseCommaSeparatedSet(str, Integer::parseInt); + private static Set parseCommaSeparatedSet(String str, + Function mapper) { + return stream(str.split(",")).map(mapper).collect(toUnmodifiableSet()); } private static > Set parseCommaSeparatedSet( - String str, Function fun, Class cls) { - return stream(str.split(",")).map(Integer::parseInt).map(fun) + String str, Function mapper, Class cls) { + return stream(str.split(",")).map(Integer::parseInt).map(mapper) .collect(toEnumSet(cls)); } @@ -349,7 +351,8 @@ private void parseLine(String line) { while (true) { m = CORE_PATTERN.matcher(rest); if (m.find() && deadCores == null) { - deadCores = parseCommaSeparatedSet(m.group("cores")); + deadCores = parseCommaSeparatedSet( + m.group("cores"), Integer::parseInt); deadCores.forEach(c -> { if (c < 0 || c >= PROCESSORS_PER_CHIP) { throw new IllegalArgumentException( @@ -489,7 +492,7 @@ public boolean isChipMentioned(ChipLocation chip) { /** @return The raw blacklist data in little-endian form. Read only. */ public ByteBuffer getRawData() { - return rawData.asReadOnlyBuffer().order(LITTLE_ENDIAN); + return readOnly(rawData); } @Override @@ -499,12 +502,8 @@ public int hashCode() { @Override public boolean equals(Object object) { - if (object instanceof Blacklist) { - var other = (Blacklist) object; - return chips.equals(other.chips) && cores.equals(other.cores) - && links.equals(other.links); - } - return false; + return (object instanceof Blacklist other) && chips.equals(other.chips) + && cores.equals(other.cores) && links.equals(other.links); } @Override @@ -524,6 +523,7 @@ public String toString() { * If output fails. * @see ObjectOutputStream#defaultWriteObject() */ + @Serial private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(rawData.remaining()); @@ -548,6 +548,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { * if the class of a serialized object could not be found. * @see ObjectInputStream#defaultReadObject() */ + @Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/CPUInfo.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/CPUInfo.java index 99657a5f25..caa83bc27e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/CPUInfo.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/CPUInfo.java @@ -36,7 +36,7 @@ /** Represents information about the state of a CPU. */ @SARKStruct("vcpu") -public class CPUInfo implements HasCoreLocation { +public final class CPUInfo implements HasCoreLocation { private final CoreLocation core; @SARKField("r") diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ChipInfo.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ChipInfo.java index 72b4cd1678..db09bb3b7e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ChipInfo.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ChipInfo.java @@ -17,7 +17,6 @@ import static java.lang.Byte.toUnsignedInt; import static java.net.InetAddress.getByAddress; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.util.Collections.sort; import static java.util.Collections.unmodifiableList; import static uk.ac.manchester.spinnaker.messages.model.DataType.ADDRESS; @@ -99,6 +98,7 @@ import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.x_size; import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.y; import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.y_size; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import java.net.InetAddress; import java.net.UnknownHostException; @@ -117,7 +117,7 @@ /** * Represents the system variables for a chip, received from the chip SDRAM. */ -public class ChipInfo implements HasChipLocation { +public final class ChipInfo implements HasChipLocation { private static final byte[] NO_IP = { 0, 0, 0, 0 }; @@ -151,7 +151,7 @@ public class ChipInfo implements HasChipLocation { * The data retrieved from SDRAM on the board. */ public ChipInfo(ByteBuffer systemData) { - this.systemData = systemData.asReadOnlyBuffer().order(LITTLE_ENDIAN); + this.systemData = readOnly(systemData); int links = read(links_available); linksAvailable = BitSet.valueOf(new byte[] { @@ -186,19 +186,12 @@ public ChipInfo(ByteBuffer systemData) { } private int read(SystemVariableDefinition var) { - switch (var.type) { - case BYTE: - return systemData.get(systemData.position() + var.offset); - case INT: - return systemData.getInt(systemData.position() + var.offset); - case SHORT: - return systemData.getShort(systemData.position() + var.offset); - case BYTE_ARRAY: - case LONG: - case ADDRESS: - default: - throw new IllegalArgumentException(); - } + return switch (var.type) { + case BYTE -> systemData.get(systemData.position() + var.offset); + case INT -> systemData.getInt(systemData.position() + var.offset); + case SHORT -> systemData.getShort(systemData.position() + var.offset); + default -> throw new IllegalArgumentException(); + }; } private long readLong(SystemVariableDefinition var) { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ChipSummaryInfo.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ChipSummaryInfo.java index 8c4331f5f3..f7e2c9cef3 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ChipSummaryInfo.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ChipSummaryInfo.java @@ -88,7 +88,7 @@ private static boolean bitset(int value, int bitnum) { private static Set parseWorkingLinks(int flags) { var wl = EnumSet.noneOf(Direction.class); - for (Direction d : Direction.values()) { + for (var d : Direction.values()) { if (bitset(flags, LINKS_FIELD_SHIFT + d.id)) { wl.add(d); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/DiagnosticFilter.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/DiagnosticFilter.java index 98e5919303..b548b9e702 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/DiagnosticFilter.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/DiagnosticFilter.java @@ -30,7 +30,7 @@ * of the destinations, sources, payload statuses, default routing statuses, * emergency routing statuses, and packet types. */ -public class DiagnosticFilter { +public final class DiagnosticFilter { private static final int PACKET_TYPE_OFFSET = 0; private static final int EMERGENCY_ROUTE_OFFSET = 4; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ExecutableTargets.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ExecutableTargets.java index e75cfff691..a5555fe3b4 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ExecutableTargets.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ExecutableTargets.java @@ -21,15 +21,14 @@ import java.util.Map; import java.util.Set; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.PositiveOrZero; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.machine.CoreSubsets; /** Encapsulate the binaries and cores on which to execute them. */ -public class ExecutableTargets { +public final class ExecutableTargets { private final Map<@NotBlank String, @Valid CoreSubsets> targets; @PositiveOrZero diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/FirmwareDescriptor.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/FirmwareDescriptor.java index fac27500af..1dc6c8bfc0 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/FirmwareDescriptor.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/FirmwareDescriptor.java @@ -19,8 +19,16 @@ import java.nio.IntBuffer; import java.time.Instant; -/** A firmware descriptor. */ -public class FirmwareDescriptor { +/** + * A firmware descriptor. + * + * @param type + * What type of firmware is this. + * @param descriptorData + * The descriptor buffer. Should be read-only. + */ +public record FirmwareDescriptor(FirmwareDescriptors type, + IntBuffer descriptorData) { private static final int MAX_OLD_STYLE = 65535; private static final int OLD_SPLIT = 100; @@ -29,10 +37,6 @@ public class FirmwareDescriptor { private static final int BYTE_SHIFT = 8; - private final FirmwareDescriptors type; - - private final IntBuffer descriptorData; - /** * Create a description of some firmware on a SpiNNaker system. * @@ -42,12 +46,11 @@ public class FirmwareDescriptor { * What was the descriptor buffer read from the system? */ public FirmwareDescriptor(FirmwareDescriptors type, ByteBuffer buffer) { - this.type = type; - descriptorData = buffer.asIntBuffer().asReadOnlyBuffer(); + this(type, buffer.asIntBuffer().asReadOnlyBuffer()); } /** @return The version of the firmware. */ - public Version getVersion() { + public Version version() { int n = descriptorData.get(type.versionIndex); if (n <= MAX_OLD_STYLE) { @@ -65,7 +68,7 @@ public Version getVersion() { } /** @return The timestamp in the firmware. */ - public Instant getTimestamp() { + public Instant timestamp() { return Instant.ofEpochSecond(descriptorData.get(type.timestampIndex)); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/HeapElement.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/HeapElement.java index 8c1f949755..3acc8aa2a4 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/HeapElement.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/HeapElement.java @@ -17,35 +17,30 @@ import uk.ac.manchester.spinnaker.machine.MemoryLocation; -/** An element of one of the heaps on SpiNNaker. */ +/** + * An element of one of the heaps on SpiNNaker. + * + * @param blockAddress + * The address of the block. + * @param nextAddress + * A pointer to the next block, or {@link MemoryLocation#NULL} if + * none. + * @param size + * The usable size of this block (not including the header). + * @param isFree + * True if the block is free. + * @param tag + * The tag of the block if allocated, or {@code null} if not. + * @param appID + * The application ID of the block if allocated, or {@code null} if + * not. + */ @SARKStruct("block") -public class HeapElement { - /** The address of the block. */ - public final MemoryLocation blockAddress; - - /** A pointer to the next block, or 0 if none. */ - @SARKField("next") - public final MemoryLocation nextAddress; - - /** The usable size of this block (not including the header). */ - public final int size; - - // Note that multiple fields are encoded in the free field. - - /** True if the block is free. */ - @SARKField("free") - public final boolean isFree; - - /** The tag of the block if allocated, or {@code null} if not. */ - @SARKField("free") - public final Integer tag; - - /** - * The application ID of the block if allocated, or {@code null} if not. - */ - @SARKField("free") - public final AppID appID; - +public record HeapElement(MemoryLocation blockAddress, + @SARKField("next") MemoryLocation nextAddress, int size, + // Note that multiple fields are encoded in the free field. + @SARKField("free") boolean isFree, @SARKField("free") Integer tag, + @SARKField("free") AppID appID) { private static final int FREE_MASK = 0xFFFF0000; private static final int BYTE_MASK = 0x000000FF; @@ -65,17 +60,16 @@ public class HeapElement { */ public HeapElement(MemoryLocation blockAddress, MemoryLocation nextAddress, int free) { - this.blockAddress = blockAddress; - this.nextAddress = nextAddress; - this.isFree = (free & FREE_MASK) != FREE_MASK; - if (isFree) { - tag = null; - appID = null; - } else { - tag = free & BYTE_MASK; - appID = new AppID((free >>> BYTE1_SHIFT) & BYTE_MASK); - } - size = nextAddress.diff(blockAddress) - BLOCK_HEADER_SIZE; + // Chain via another constructor so we have convenient access to isFree + this(blockAddress, nextAddress, free, (free & FREE_MASK) != FREE_MASK); + } + + private HeapElement(MemoryLocation blockAddress, MemoryLocation nextAddress, + int free, boolean isFree) { + this(blockAddress, nextAddress, + nextAddress.diff(blockAddress) - BLOCK_HEADER_SIZE, isFree, + isFree ? null : free & BYTE_MASK, + isFree ? null : new AppID((free >>> BYTE1_SHIFT) & BYTE_MASK)); } /** @@ -84,7 +78,7 @@ public HeapElement(MemoryLocation blockAddress, MemoryLocation nextAddress, * @return The address of the data ({@link #size} bytes long) that * immediately follows the heap element header. */ - public final MemoryLocation getDataAddress() { + public final MemoryLocation dataAddress() { return blockAddress.add(BLOCK_HEADER_SIZE); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/IOBuffer.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/IOBuffer.java index 7bb412c1ef..58836ecb70 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/IOBuffer.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/IOBuffer.java @@ -16,8 +16,8 @@ package uk.ac.manchester.spinnaker.messages.model; import static java.nio.ByteBuffer.wrap; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.nio.charset.StandardCharsets.US_ASCII; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -32,7 +32,7 @@ * * @author Donal Fellows */ -public class IOBuffer implements HasCoreLocation { +public final class IOBuffer implements HasCoreLocation { private final HasCoreLocation core; private final byte[] iobuf; @@ -57,7 +57,7 @@ public IOBuffer(HasCoreLocation core, byte[] contents) { public IOBuffer(HasCoreLocation core, ByteBuffer contents) { this.core = core; iobuf = new byte[contents.remaining()]; - contents.asReadOnlyBuffer().order(LITTLE_ENDIAN).get(iobuf); + readOnly(contents).get(iobuf); } /** @@ -108,7 +108,7 @@ public InputStream getContentsStream() { * buffer. */ public ByteBuffer getContentsBuffer() { - return wrap(iobuf).asReadOnlyBuffer().order(LITTLE_ENDIAN); + return readOnly(wrap(iobuf)); } /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/MemoryAllocationFailedException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/MemoryAllocationFailedException.java index 3d2377a598..e08ba2ce3b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/MemoryAllocationFailedException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/MemoryAllocationFailedException.java @@ -15,10 +15,13 @@ */ package uk.ac.manchester.spinnaker.messages.model; +import java.io.Serial; + /** * Indicate that a memory allocation operation has failed. */ public class MemoryAllocationFailedException extends Exception { + @Serial private static final long serialVersionUID = 4463116302552127934L; /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/P2PTable.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/P2PTable.java index dfa86489af..592ba687c3 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/P2PTable.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/P2PTable.java @@ -20,6 +20,7 @@ import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; import static uk.ac.manchester.spinnaker.messages.model.P2PTableRoute.NONE; import static uk.ac.manchester.spinnaker.utils.CollectionUtils.copy; +import static uk.ac.manchester.spinnaker.utils.MathUtils.ceildiv; import java.nio.ByteBuffer; import java.util.Collection; @@ -34,7 +35,7 @@ import uk.ac.manchester.spinnaker.machine.ValidMachineWidth; /** Represents a P2P routing table read from the machine. */ -public class P2PTable { +public final class P2PTable { private final Map routes; /** The width of the machine that this table represents. */ @@ -67,8 +68,8 @@ public class P2PTable { public P2PTable(MachineDimensions dimensions, Collection columnData) { this.routes = new HashMap<>(); - this.width = dimensions.width; - this.height = dimensions.height; + this.width = dimensions.width(); + this.height = dimensions.height(); parseColumnData(columnData); } @@ -101,7 +102,7 @@ private void extractRoutes(int chipX, int chipYBase, int word) { * @return The number of bytes for the column */ public static int getNumColumnBytes(int height) { - return ((height + NBBY - 1) / NBBY) * WORD_SIZE; + return ceildiv(height, NBBY) * WORD_SIZE; } /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ReinjectionStatus.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ReinjectionStatus.java index 4a351208ce..ac6d966e1f 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ReinjectionStatus.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/ReinjectionStatus.java @@ -20,8 +20,37 @@ /** * Represents a status information message obtained from the dropped packet * reinjection core (an "extra monitor" core). + * + * @param timeout + * The WAIT1 timeout value of the router in cycles. + * @param emergencyTimeout + * The WAIT2 timeout value of the router in cycles. + * @param droppedPackets + * The number of packets dropped by the router and received by the + * reinjection functionality (may not fit in the queue though). + * @param missedDroppedPackets + * The number of times that when a dropped packet was read it was + * found that another one or more packets had also been dropped, but + * had been missed. + * @param droppedPacketOverflows + * Of the {@link #droppedPackets} received, how many were lost due to + * not having enough space in the queue of packets to reinject. + * @param reinjectedPackets + * Of the {@link #droppedPackets} received, how many packets were + * successfully reinjected. + * @param linkDumps + * The number of times that when a dropped packet was caused due to a + * link failing to take the packet. + * @param processorDumps + * The number of times that when a dropped packet was caused due to a + * processor failing to take the packet. + * @param flags + * The flags that states which types of packets were being recorded. */ -public class ReinjectionStatus { +public record ReinjectionStatus(RouterTimeout timeout, + RouterTimeout emergencyTimeout, int droppedPackets, + int missedDroppedPackets, int droppedPacketOverflows, + int reinjectedPackets, int linkDumps, int processorDumps, int flags) { /** Used to pick low nybble of value. */ static final int MASK = 0xF; @@ -42,124 +71,15 @@ public static int encodeTimeout(int mantissa, int exponent) { return (mantissa & MASK) | ((exponent & MASK) << SHIFT); } - /** The WAIT1 timeout value of the router in cycles. */ - private final RouterTimeout timeout; - - /** The WAIT2 timeout value of the router in cycles. */ - private final RouterTimeout emergencyTimeout; - - /** - * The number of packets dropped by the router and received by the - * reinjection functionality (may not fit in the queue though). - */ - private final int droppedPackets; - - /** - * The number of times that when a dropped packet was read it was found that - * another one or more packets had also been dropped, but had been missed. - */ - private final int missedDroppedPackets; - - /** - * Of the {@link #droppedPackets} received, how many were lost due to not - * having enough space in the queue of packets to reinject. - */ - private final int droppedPacketOverflows; - - /** - * Of the {@link #droppedPackets} received, how many packets were - * successfully reinjected. - */ - private final int reinjectedPackets; - - /** - * The number of times that when a dropped packet was caused due to a link - * failing to take the packet. - */ - private final int linkDumps; - - /** - * The number of times that when a dropped packet was caused due to a - * processor failing to take the packet. - */ - private final int processorDumps; - - /** The flags that states which types of packets were being recorded. */ - private final int flags; - /** * @param buffer * The message containing the status to parse. */ public ReinjectionStatus(ByteBuffer buffer) { - this.timeout = new RouterTimeout(buffer.getInt()); - this.emergencyTimeout = new RouterTimeout(buffer.getInt()); - this.droppedPackets = buffer.getInt(); - this.missedDroppedPackets = buffer.getInt(); - this.droppedPacketOverflows = buffer.getInt(); - this.reinjectedPackets = buffer.getInt(); - this.linkDumps = buffer.getInt(); - this.processorDumps = buffer.getInt(); - this.flags = buffer.getInt(); - } - - /** @return The WAIT1 timeout value of the router in cycles. */ - public RouterTimeout getTimeout() { - return timeout; - } - - /** @return The WAIT2 timeout value of the router in cycles. */ - public RouterTimeout getEmergencyTimeout() { - return emergencyTimeout; - } - - /** - * @return The number of packets dropped by the router and received by the - * reinjection functionality (may not fit in the queue though). - */ - public int getNumDroppedPackets() { - return droppedPackets; - } - - /** - * @return The number of times that when a dropped packet was read it was - * found that another one or more packets had also been dropped, but - * had been missed. - */ - public int getNumMissedDroppedPackets() { - return missedDroppedPackets; - } - - /** - * @return Of the n_dropped_packets received, how many were lost due to not - * having enough space in the queue of packets to reinject. - */ - public int getNumDroppedPacketOverflows() { - return droppedPacketOverflows; - } - - /** - * @return The number of times that when a dropped packet was caused due to - * a processor failing to take the packet. - */ - public int getNumProcessorDumps() { - return processorDumps; - } - - /** - * @return The number of times that when a dropped packet was caused due to - * a link failing to take the packet. - */ - public int getNumLinkDumps() { - return linkDumps; - } - - /** - * @return Of the number of dropped packets received, how many packets were - * successfully reinjected. - */ - public int getNumReinjectedPackets() { - return reinjectedPackets; + this(new RouterTimeout(buffer.getInt()), + new RouterTimeout(buffer.getInt()), buffer.getInt(), + buffer.getInt(), buffer.getInt(), buffer.getInt(), + buffer.getInt(), buffer.getInt(), buffer.getInt()); } private boolean flag(DPRIFlags flag) { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/RouterDiagnostics.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/RouterDiagnostics.java index d4000e31c4..fadcd30ff7 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/RouterDiagnostics.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/RouterDiagnostics.java @@ -37,7 +37,7 @@ /** * Represents a set of diagnostic information available from a chip router. */ -public class RouterDiagnostics { +public final class RouterDiagnostics { /** The "mon" part of the control register. */ public final int mon; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/RouterTimeout.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/RouterTimeout.java index 302bd9ca8c..7383e4a8b6 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/RouterTimeout.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/RouterTimeout.java @@ -22,8 +22,12 @@ * A router timeout, originally stored as an 8-bit unsigned float. * * @author Donal Fellows + * @param mantissa + * The mantissa of the timeout. + * @param exponent + * The exponent of the timeout. */ -public final class RouterTimeout { +public record RouterTimeout(byte mantissa, byte exponent) { private static final int MANTISSA_OFFSET = 16; private static final int EXPONENT_OFFSET = 4; @@ -33,15 +37,8 @@ public final class RouterTimeout { private static final double CLOCK_INTERVAL = 1e9 / 133e6; - /** The mantissa of the timeout. */ - public final int mantissa; - - /** The exponent of the timeout. */ - public final int exponent; - RouterTimeout(int encodedValue) { - mantissa = encodedValue & MASK; - exponent = (encodedValue >> SHIFT) & MASK; + this(encodedValue, encodedValue >> SHIFT); } /** @@ -51,14 +48,13 @@ public final class RouterTimeout { * The exponent of the value; only low 4 bits used. */ public RouterTimeout(int mantissa, int exponent) { - this.mantissa = mantissa & MASK; - this.exponent = exponent & MASK; + this((byte) (mantissa & MASK), (byte) (exponent & MASK)); } /** * @return the timeout value of a router in ticks. */ - public int getValue() { + public int value() { int m = mantissa + MANTISSA_OFFSET; if (exponent <= EXPONENT_OFFSET) { return (m - (1 << (EXPONENT_OFFSET - exponent))) * (1 << exponent); @@ -71,6 +67,6 @@ public String toString() { if (mantissa == INF.mantissa && exponent == INF.exponent) { return "INF"; } - return "" + (getValue() * CLOCK_INTERVAL) + " ns"; + return "" + (value() * CLOCK_INTERVAL) + " ns"; } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SerialVector.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/SerialVector.java similarity index 52% rename from SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SerialVector.java rename to SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/SerialVector.java index c4e8328b94..105ccef846 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/bmp/SerialVector.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/SerialVector.java @@ -13,65 +13,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package uk.ac.manchester.spinnaker.messages.bmp; +package uk.ac.manchester.spinnaker.messages.model; -import java.nio.ByteBuffer; import java.nio.IntBuffer; import uk.ac.manchester.spinnaker.machine.MemoryLocation; -import uk.ac.manchester.spinnaker.messages.scp.SCPCommand; -import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; +import uk.ac.manchester.spinnaker.messages.bmp.ReadADC; +import uk.ac.manchester.spinnaker.messages.bmp.ReadSerialVector; /** - * The data in the serial vector. The result of a {@link SCPCommand#CMD_BMP_INFO - * BMP_INFO} call with argument {@link BMPInfo#SERIAL SERIAL}. + * The data in the BMP serial vector. *

* See {@code cmd_bmp_info()} in {@code bmp_cmd.c}. * + * @param hardwareVersion + * Hardware version. + * @param serialNumber + * LPC1768 serial number. Length {@value #SERIAL_LENGTH}. + * @param flashBuffer + * Flash buffer address. + * @param boardStat + * {@code board_stat} address for the board. See {@link ReadADC} for + * an operation that reads this. + * @param cortexBoot + * Cortex boot vector address. Can be used to determine which copy of + * the BMP code was successfully booted from. * @see ReadSerialVector */ -@UsedInJavadocOnly(SCPCommand.class) -public class SerialVector { - /** The number of words in the {@link #getSerialNumber() serial_number}. */ +public record SerialVector(int hardwareVersion, IntBuffer serialNumber, + MemoryLocation flashBuffer, MemoryLocation boardStat, + MemoryLocation cortexBoot) { + /** The number of words in the {@link #serialNumber() serial_number}. */ public static final int SERIAL_LENGTH = 4; - /** Hardware version. */ - private final int hardwareVersion; - - /** LPC1768 serial number. Length 4. */ - private final IntBuffer serialNumber; - - /** Flash buffer address. */ - private final MemoryLocation flashBuffer; - - /** {@code board_stat} address for the board. */ - private final MemoryLocation boardStat; - - /** Cortex boot vector address. */ - private final MemoryLocation cortexBoot; - /** - * @param buffer - * Where to deserialize the vector from. - */ - SerialVector(ByteBuffer buffer) { - var b = buffer.asIntBuffer(); - hardwareVersion = b.get(); - var sn = new int[SERIAL_LENGTH]; - b.get(sn); - serialNumber = IntBuffer.wrap(sn); - flashBuffer = new MemoryLocation(b.get()); - boardStat = new MemoryLocation(b.get()); - cortexBoot = new MemoryLocation(b.get()); - } - - /** @return The hardware version. */ - public int getHardwareVersion() { - return hardwareVersion; - } - - /** - * @return The device serial number, as a read-only buffer. + * @return The device serial number, as a read-only buffer. Length + * {@value #SERIAL_LENGTH}. */ // @formatter:off /* Obtained from LPC17xx In Application Programming function 58. The API @@ -94,26 +71,8 @@ public int getHardwareVersion() { * The four words of the result form the four words provided below, in the * order described above (not that that typically matters). */ // @formatter:on - public IntBuffer getSerialNumber() { + public IntBuffer serialNumber() { // Make a new instance so positions aren't shared return serialNumber.asReadOnlyBuffer(); } - - /** @return The location of the flash buffer. */ - public MemoryLocation getFlashBuffer() { - return flashBuffer; - } - - /** - * @return The board status block location. - * @see ReadADC - */ - public MemoryLocation getBoardStatusLocation() { - return boardStat; - } - - /** @return The location of the Cortex boot vector. */ - public MemoryLocation getCortexVector() { - return cortexBoot; - } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/SystemVariableDefinition.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/SystemVariableDefinition.java index d218362f2f..83f4626f89 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/SystemVariableDefinition.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/SystemVariableDefinition.java @@ -281,19 +281,16 @@ public Object getDefault() { this.type = type; this.offset = offset; hasDefinedDefault = true; - switch (type) { - case ADDRESS: - this.defAddr = new MemoryLocation(((Number) def).intValue()); - this.def = 0; - break; - case BYTE_ARRAY: - this.defAddr = NULL; - this.def = 0; - break; - default: - this.defAddr = NULL; - this.def = ((Number) def).intValue(); - } + + this.def = switch (type) { + case ADDRESS, BYTE_ARRAY -> 0; + default -> ((Number) def).intValue(); + }; + + defAddr = switch (type) { + case ADDRESS -> new MemoryLocation(((Number) def).intValue()); + default -> NULL; + }; } /** @@ -303,17 +300,16 @@ public Object getDefault() { * @return The default value, or a copy of it if the type of the value is an * array. */ - @SuppressWarnings("checkstyle:JavadocMethod") // AssertionError not in signature because it should be unreachable public Object getDefault() { - switch (type) { - case BYTE_ARRAY: + return switch (type) { + // CHECKSTYLE:OFF + case BYTE_ARRAY -> throw new AssertionError("unreachable location reached"); - case ADDRESS: - return defAddr; - default: - return def; - } + // CHECKSTYLE:ON + case ADDRESS -> defAddr; + default -> def; + }; } /** @return Whether this is a variable with a usefully-defined default. */ @@ -337,28 +333,15 @@ public boolean isDefaultSpecified() { */ public void addToBuffer(Object value, ByteBuffer buffer) { switch (type) { - case BYTE: - buffer.put(((Number) value).byteValue()); - return; - case SHORT: - buffer.putShort(((Number) value).shortValue()); - return; - case INT: - buffer.putInt(((Number) value).intValue()); - return; - case LONG: - buffer.putLong(((Number) value).longValue()); - return; - case BYTE_ARRAY: - buffer.put((byte[]) value); - return; - case ADDRESS: - buffer.putInt(((MemoryLocation) value).address); - return; - default: - // CHECKSTYLE:OFF - throw new Error("unreachable?"); - // CHECKSTYLE:ON + case BYTE -> buffer.put(((Number) value).byteValue()); + case SHORT -> buffer.putShort(((Number) value).shortValue()); + case INT -> buffer.putInt(((Number) value).intValue()); + case LONG -> buffer.putLong(((Number) value).longValue()); + case BYTE_ARRAY -> buffer.put((byte[]) value); + case ADDRESS -> buffer.putInt(((MemoryLocation) value).address()); + // CHECKSTYLE:OFF + default -> throw new Error("unreachable?"); + // CHECKSTYLE:ON } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TagDescription.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TagDescription.java index 29b856c8fe..87fe2919db 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TagDescription.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TagDescription.java @@ -15,111 +15,50 @@ */ package uk.ac.manchester.spinnaker.messages.model; -import static java.net.InetAddress.getByAddress; -import static uk.ac.manchester.spinnaker.messages.model.IPTagFieldDefinitions.CORE_MASK; -import static uk.ac.manchester.spinnaker.messages.model.IPTagFieldDefinitions.PORT_SHIFT; -import static uk.ac.manchester.spinnaker.messages.model.IPTagFieldDefinitions.THREE_BITS_MASK; - import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.CoreLocation; -import uk.ac.manchester.spinnaker.machine.HasChipLocation; -import uk.ac.manchester.spinnaker.machine.HasCoreLocation; import uk.ac.manchester.spinnaker.machine.tags.IPTag; import uk.ac.manchester.spinnaker.machine.tags.ReverseIPTag; import uk.ac.manchester.spinnaker.machine.tags.Tag; -/** Description of a tag. */ -public final class TagDescription { - // TODO convert to a record in Java 17 - /** - * The count of the number of packets that have been sent with the tag. - */ - public final int count; - - /** The flags of the tag. */ - public final short flags; - - /** The IP address of the tag. */ - public final InetAddress ipAddress; - - /** The MAC address of the tag, as an array of 6 bytes. */ - public final byte[] macAddress; - - /** The port of the tag. */ - public final int port; - - /** The receive port of the tag. */ - public final int rxPort; - - /** - * The location of the core on the chip which the tag is defined on and - * where the core that handles the tag's messages resides. - */ - public final HasCoreLocation spinCore; - - /** The spin-port of the IP tag. */ - public final int spinPort; - - /** The timeout of the tag. */ - public final IPTagTimeOutWaitTime timeout; - - /** Where did the message that we've parsed this from originate from? */ - private final ChipLocation src; - - /** - * On what SpiNNaker board did this info originate? Assumes we were talking - * directly to the board. - */ - private final InetAddress host; - - /** What tag was this info about? */ - private final int tag; - - /** - * @param buffer - * The buffer to parse. - * @param src - * What chip did this come from? - * @param host - * What address did that chip have? (Only for creating a - * {@link Tag}.) - * @param tag - * What was the tag ID? (Only for creating a {@link Tag}.) - * @throws UnknownHostException - * If we can't parse the host. Unexpected. - */ - public TagDescription(ByteBuffer buffer, HasChipLocation src, - InetAddress host, int tag) throws UnknownHostException { - byte[] ipBytes = new byte[IPV4_BYTES]; - buffer.get(ipBytes); - ipAddress = getByAddress(ipBytes); - - macAddress = new byte[MAC_BYTES]; - buffer.get(macAddress); - - port = Short.toUnsignedInt(buffer.getShort()); - timeout = IPTagTimeOutWaitTime.get(buffer.getShort()); - flags = buffer.getShort(); - count = buffer.getInt(); - rxPort = Short.toUnsignedInt(buffer.getShort()); - int y = Byte.toUnsignedInt(buffer.get()); - int x = Byte.toUnsignedInt(buffer.get()); - int pp = Byte.toUnsignedInt(buffer.get()); - spinCore = new CoreLocation(x, y, pp & CORE_MASK); - spinPort = (pp >>> PORT_SHIFT) & THREE_BITS_MASK; - this.src = src.asChipLocation(); - this.host = host; - this.tag = tag; - } - - private static final int IPV4_BYTES = 4; - - private static final int MAC_BYTES = 6; - +/** + * Description of a tag. + * + * @param count + * The count of the number of packets that have been sent with the + * tag. + * @param flags + * The flags of the tag. + * @param ipAddress + * The IP address of the tag. + * @param macAddress + * The MAC address of the tag, as an array of 6 bytes. + * @param port + * The port of the tag. + * @param rxPort + * The receive port of the tag. + * @param spinCore + * The location of the core on the chip which the tag is defined on + * and where the core that handles the tag's messages resides. + * @param spinPort + * The spin-port of the IP tag. + * @param timeout + * The timeout of the tag. + * @param src + * Where did the message that we've parsed this from originate from? + * @param host + * On what SpiNNaker board did this info originate? Assumes we were + * talking directly to the board. + * @param tagId + * What tag was this info about? + * @author Donal Fellows + */ +public record TagDescription(int count, short flags, InetAddress ipAddress, + byte[] macAddress, int port, int rxPort, CoreLocation spinCore, + int spinPort, IPTagTimeOutWaitTime timeout, ChipLocation src, + InetAddress host, int tagId) { private static final int USE_BIT = 15; private static final int TEMP_BIT = 14; @@ -135,12 +74,12 @@ private boolean bitset(int bit) { } /** @return True if the tag is marked as being in use. */ - public boolean isInUse() { + public boolean inUse() { return bitset(USE_BIT); } /** @return True if the tag is temporary. */ - public boolean isTemporary() { + public boolean temporary() { return bitset(TEMP_BIT); } @@ -148,32 +87,32 @@ public boolean isTemporary() { * @return True if the tag is in the ARP state (where the MAC address is * being looked up; this is a transient state so unlikely). */ - public boolean isARP() { + public boolean arp() { return bitset(ARP_BIT); } /** @return True if the tag is a reverse tag. */ - public boolean isReverse() { + public boolean reverse() { return bitset(REV_BIT); } /** @return True if the tag is to strip the SDP header. */ - public boolean isStrippingSDP() { + public boolean strippingSDP() { return bitset(STRIP_BIT); } /** * Get the standard tag descriptor. Not properly meaningful unless the tag - * is {@linkplain #isInUse() in use}. + * is {@linkplain #inUse() in use}. * * @return The tag descriptor. May be an {@link IPTag} or a * {@link ReverseIPTag}. */ - public Tag getTag() { - if (isReverse()) { - return new ReverseIPTag(host, tag, rxPort, spinCore, spinPort); + public Tag tag() { + if (reverse()) { + return new ReverseIPTag(host, tagId, rxPort, spinCore, spinPort); } else { - return new IPTag(host, src, tag, ipAddress, port, isStrippingSDP()); + return new IPTag(host, src, tagId, ipAddress, port, strippingSDP()); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TagInfo.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TagInfo.java index ab7e5111c9..c859cb640f 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TagInfo.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TagInfo.java @@ -17,33 +17,19 @@ import com.google.errorprone.annotations.Immutable; -/** Information about a tag pool on an Ethernet-connected chip. */ +/** + * Information about a tag pool on an Ethernet-connected chip. + * + * @param transientTimeout + * The timeout for transient IP tags (i.e., responses to SCP + * commands). + * @param poolSize + * The count of the IP tag pool size. + * @param fixedSize + * The count of the number of fixed IP tag entries. + * @author Donal Fellows + */ @Immutable -public final class TagInfo { - /** - * The timeout for transient IP tags (i.e., responses to SCP commands). - */ - public final IPTagTimeOutWaitTime transientTimeout; - - /** The count of the IP tag pool size. */ - public final int poolSize; - - /** The count of the number of fixed IP tag entries. */ - public final int fixedSize; - - /** - * @param transientTimeout - * The timeout for transient IP tags (i.e., responses to SCP - * commands). - * @param poolSize - * The count of the IP tag pool size. - * @param fixedSize - * The count of the number of fixed IP tag entries. - */ - public TagInfo(IPTagTimeOutWaitTime transientTimeout, int poolSize, - int fixedSize) { - this.transientTimeout = transientTimeout; - this.poolSize = poolSize; - this.fixedSize = fixedSize; - } +public record TagInfo(IPTagTimeOutWaitTime transientTimeout, int poolSize, + int fixedSize) { } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TransferUnit.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TransferUnit.java index 6e7b4e3190..bad4c23280 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TransferUnit.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/TransferUnit.java @@ -62,7 +62,7 @@ public static TransferUnit efficientTransferUnit(MemoryLocation address, int size) { if (address.isAligned() && size % WORD_SIZE == 0) { return WORD; - } else if (odd(address.address) || odd(size)) { + } else if (odd(address.address()) || odd(size)) { return BYTE; } else { return HALF_WORD; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/UnexpectedResponseCodeException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/UnexpectedResponseCodeException.java index 10771e9210..c5d8542087 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/UnexpectedResponseCodeException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/UnexpectedResponseCodeException.java @@ -17,6 +17,8 @@ import static java.lang.String.format; +import java.io.Serial; + import uk.ac.manchester.spinnaker.messages.scp.SCPResult; /** @@ -24,6 +26,7 @@ * current operation. */ public class UnexpectedResponseCodeException extends Exception { + @Serial private static final long serialVersionUID = 7864690081287752744L; /** The response that cause this exception to be thrown, if known. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/UnroutableMessageException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/UnroutableMessageException.java index f4e540bc7f..a17adca075 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/UnroutableMessageException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/UnroutableMessageException.java @@ -15,6 +15,8 @@ */ package uk.ac.manchester.spinnaker.messages.model; +import java.io.Serial; + import uk.ac.manchester.spinnaker.messages.scp.SCPResult; import uk.ac.manchester.spinnaker.messages.sdp.SDPHeader; @@ -24,6 +26,7 @@ */ public class UnroutableMessageException extends UnexpectedResponseCodeException { + @Serial private static final long serialVersionUID = 2106128799950032057L; /** The full header from the response message. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Version.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Version.java index b749112f06..76ac59eca6 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Version.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/Version.java @@ -20,45 +20,35 @@ import java.util.regex.Pattern; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; /** * A three-part semantic version description. * * @author Donal Fellows + * @param majorVersion + * The major version number. Two versions are not compatible if they + * have different major version numbers. Major version number + * differences dominate. The major version is supposed to only be + * updated when an incompatible API change occurs. + * @param minorVersion + * The minor version number. A version is compatible with another + * version if it has the same major version and a minor version that + * is greater than or equal to the other minor version. The minor + * version is supposed to be updated when a compatible API change + * occurs. + * @param revision + * The revision number. Less important than the minor version number. + * Two versions are usually compatible if they have the same major + * and minor version, with the revision being typically unimportant + * for that decision. Revisions should be updated when a new release + * happens, even if that only contains bug fixes and no API changes. */ -public final class Version implements Comparable { +public record Version(@JsonProperty("major-version") int majorVersion, + @JsonProperty("minor-version") int minorVersion, + @JsonProperty("revision") int revision) implements Comparable { // There is no standard Version class. WRYYYYYYYYYYYYYYYY!!!! - /** - * The major version number. Two versions are not compatible if they have - * different major version numbers. Major version number differences - * dominate. - */ - public final int majorVersion; - - /** The minor version number. */ - public final int minorVersion; - - /** The revision number. Less important than the minor version number. */ - public final int revision; - - /** - * Create a version number. - * - * @param major - * the major number - * @param minor - * the minor number - * @param rev - * the revision number - */ - public Version(@JsonProperty("major-version") int major, - @JsonProperty("minor-version") int minor, - @JsonProperty("revision") int rev) { - majorVersion = major; - minorVersion = minor; - revision = rev; - } /** * Create a version number. @@ -71,19 +61,17 @@ public Version(@JsonProperty("major-version") int major, * the revision number */ public Version(String major, String minor, String rev) { - majorVersion = parseInt(major); - minorVersion = parseInt(minor); - revision = parseInt(rev); + this(parseInt(major), parseInt(minor), parseInt(rev)); } // This RE is in Extended mode syntax, which COMMENTS enables - private static final Pattern VERSION_RE = Pattern.compile( - " ^(\"?) # A optional quote\n" - + "(?\\d+) # A major version number\n" - + "(?:\\.(?\\d+) # An optional minor version number\n" - + "(?:\\.(?\\d+))? # An optional revision number\n" - + ")?\\1$ # Back reference to optional quote\n", - Pattern.COMMENTS); + private static final Pattern VERSION_RE = Pattern.compile(""" + ^("?) # A optional quote + (?\\d+) # A major version number + (?:\\.(?\\d+) # An optional minor version number + (?:\\.(?\\d+))? # An optional revision number + )? \\1 $ # Back reference to optional quote + """, Pattern.COMMENTS); /** * Create a version number. @@ -91,19 +79,21 @@ public Version(String major, String minor, String rev) { * @param threePartVersion * the version identifier, as {@code X} or {@code X.Y} or * {@code X.Y.Z}. + * @return The deserialised value. * @throws IllegalArgumentException * If the version string doesn't match one of the supported * patterns. */ - public Version(String threePartVersion) { + @JsonCreator + public static Version parse(String threePartVersion) { var m = VERSION_RE.matcher(threePartVersion); if (!m.matches()) { throw new IllegalArgumentException( "bad version string: " + threePartVersion); } - majorVersion = parseInt(m.group("major")); - minorVersion = parsePossibleInt(m.group("minor")); - revision = parsePossibleInt(m.group("revision")); + return new Version(parseInt(m.group("major")), + parsePossibleInt(m.group("minor")), + parsePossibleInt(m.group("revision"))); } private static int parsePossibleInt(String s) { @@ -113,21 +103,6 @@ private static int parsePossibleInt(String s) { return parseInt(s); } - @Override - public boolean equals(Object other) { - if (!(other instanceof Version)) { - return false; - } - var v = (Version) other; - return majorVersion == v.majorVersion && minorVersion == v.minorVersion - && revision == v.revision; - } - - @Override - public int hashCode() { - return (majorVersion << 10) ^ (minorVersion << 5) ^ revision; - } - @Override public int compareTo(Version other) { int cmp = compare(majorVersion, other.majorVersion); @@ -142,6 +117,21 @@ public int compareTo(Version other) { @Override public String toString() { - return "" + majorVersion + "." + minorVersion + "." + revision; + return majorVersion + "." + minorVersion + "." + revision; + } + + /** + * Determine whether this version is compatible with the given requirement. + * For a version to match a requirement, it must have the same major version + * and a minor version/revision that is at least what the requirement + * states. + * + * @param requirement + * The version that we are testing for compatibility with. + * @return True if they are compatible, false otherwise. + */ + public boolean compatibleWith(Version requirement) { + return (majorVersion == requirement.majorVersion) + && compareTo(requirement) >= 0; } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/VersionInfo.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/VersionInfo.java index 6194df6c53..8d33706cf1 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/VersionInfo.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/model/VersionInfo.java @@ -17,11 +17,9 @@ import static java.lang.Byte.toUnsignedInt; import static java.lang.Short.toUnsignedInt; +import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.time.Instant.ofEpochSecond; -import static java.time.ZoneOffset.UTC; -import static java.time.ZonedDateTime.ofInstant; -import static java.time.format.DateTimeFormatter.ISO_INSTANT; import java.nio.ByteBuffer; import java.util.regex.Pattern; @@ -138,10 +136,8 @@ public VersionInfo(ByteBuffer buffer, boolean isBMP) { @Override public String toString() { - return "VersionInfo(" + location + " (phys:" + physicalCPUID - + "), version: " + versionNumber + ", " + name + "/" + hardware - + ", " + ofInstant(ofEpochSecond(buildDate, 0), UTC) - .format(ISO_INSTANT) - + ")"; + return format("VersionInfo(%s (phys:%d), version: %s, %s/%s, %s)", + location, physicalCPUID, versionNumber, name, hardware, + ofEpochSecond(buildDate)); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/AbstractNotificationMessage.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/AbstractNotificationMessage.java deleted file mode 100644 index c1abb343fa..0000000000 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/AbstractNotificationMessage.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2022 The University of Manchester - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package uk.ac.manchester.spinnaker.messages.notification; - -import java.nio.ByteBuffer; - -import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; - -/** - * A base class for concrete notification messages. - * - * @author Donal Fellows - */ -public abstract class AbstractNotificationMessage - implements NotificationMessage { - // Must be power of 2 (minus 1) - private static final int MAX_COMMAND = 0x3FFF; - - private static final int FLAG1_BIT = 15; - - private static final int FLAG2_BIT = 14; - - /** The command code of the message. */ - private final NotificationMessageCode command; - - /** - * @param buffer - * Where to read the command code from. The command code will be - * in the first two bytes; this constructor advances the buffer - * position. - */ - protected AbstractNotificationMessage(ByteBuffer buffer) { - command = NotificationMessageCode.get(buffer.getShort() & MAX_COMMAND); - } - - /** - * @param command - * The command code of the message. - */ - public AbstractNotificationMessage(NotificationMessageCode command) { - this.command = command; - } - - @Override - @OverridingMethodsMustInvokeSuper - public void addToBuffer(ByteBuffer buffer) { - short value = (short) (0 << FLAG1_BIT | 1 << FLAG2_BIT - | command.getValue()); - buffer.putShort(value); - } - - /** - * Create a notification message instance from a buffer holding a received - * message. - * - * @param buffer - * The received message data buffer. - * @return The parsed message. - * @throws UnsupportedOperationException - * If the message is not understood. - */ - public static NotificationMessage build(ByteBuffer buffer) { - switch (NotificationMessageCode.get(buffer.getShort(0) & MAX_COMMAND)) { - case DATABASE_CONFIRMATION: - return new DatabaseConfirmation(buffer); - case START_RESUME_NOTIFICATION: - return new StartResume(buffer); - case STOP_PAUSE_NOTIFICATION: - return new PauseStop(buffer); - default: - throw new UnsupportedOperationException(); - } - } -} diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/DatabaseConfirmation.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/DatabaseConfirmation.java index 0b9702b592..fd87e9878a 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/DatabaseConfirmation.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/DatabaseConfirmation.java @@ -27,7 +27,7 @@ * the toolchain which is to be used by any software which interfaces with * SpiNNaker. */ -public class DatabaseConfirmation extends AbstractNotificationMessage { +public final class DatabaseConfirmation extends NotificationMessage { /** * The path to the database, as seen by the sender's filesystem. Note that * there is a length limit; the overall message must fit in a UDP message. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/NotificationMessage.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/NotificationMessage.java index efb9e51ba5..b34d3e71fb 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/NotificationMessage.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/NotificationMessage.java @@ -15,12 +15,74 @@ */ package uk.ac.manchester.spinnaker.messages.notification; +import java.nio.ByteBuffer; + +import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; + import uk.ac.manchester.spinnaker.messages.SerializableMessage; /** * A notification message. Note that the general constraint on the length of * these messages is set by UDP and, possibly, Ethernet, not SpiNNaker; these * messages are not routed via a SpiNNaker board. + * + * @author Donal Fellows */ -public interface NotificationMessage extends SerializableMessage { +public abstract sealed class NotificationMessage implements SerializableMessage + permits DatabaseConfirmation, PauseStop, StartResume { + // Must be power of 2 (minus 1) + private static final int MAX_COMMAND = 0x3FFF; + + private static final int FLAG1_BIT = 15; + + private static final int FLAG2_BIT = 14; + + /** The command code of the message. */ + private final NotificationMessageCode command; + + /** + * @param buffer + * Where to read the command code from. The command code will be + * in the first two bytes; this constructor advances the buffer + * position. + */ + protected NotificationMessage(ByteBuffer buffer) { + command = NotificationMessageCode.get(buffer.getShort() & MAX_COMMAND); + } + + /** + * @param command + * The command code of the message. + */ + public NotificationMessage(NotificationMessageCode command) { + this.command = command; + } + + @Override + @OverridingMethodsMustInvokeSuper + public void addToBuffer(ByteBuffer buffer) { + short value = (short) (0 << FLAG1_BIT | 1 << FLAG2_BIT + | command.getValue()); + buffer.putShort(value); + } + + /** + * Create a notification message instance from a buffer holding a received + * message. + * + * @param buffer + * The received message data buffer. + * @return The parsed message. + * @throws UnsupportedOperationException + * If the message is not understood. + */ + public static NotificationMessage build(ByteBuffer buffer) { + return switch (NotificationMessageCode + .get(buffer.getShort(0) & MAX_COMMAND)) { + case DATABASE_CONFIRMATION -> new DatabaseConfirmation(buffer); + case START_RESUME_NOTIFICATION -> new StartResume(buffer); + case STOP_PAUSE_NOTIFICATION -> new PauseStop(buffer); + default -> throw new UnsupportedOperationException(); + }; + } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/PauseStop.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/PauseStop.java index 06de9f3d83..96d7ef47d6 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/PauseStop.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/PauseStop.java @@ -22,7 +22,7 @@ /** * Message which indicates that the toolchain has paused or stopped. */ -public class PauseStop extends AbstractNotificationMessage { +public final class PauseStop extends NotificationMessage { /** Create an instance. */ public PauseStop() { super(STOP_PAUSE_NOTIFICATION); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/StartResume.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/StartResume.java index b76cd8bda5..31c6973fd3 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/StartResume.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/notification/StartResume.java @@ -22,7 +22,7 @@ /** * Message which indicates that the toolchain has started or resumed. */ -public class StartResume extends AbstractNotificationMessage { +public final class StartResume extends NotificationMessage { /** Create an instance. */ public StartResume() { super(START_RESUME_NOTIFICATION); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ApplicationRun.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ApplicationRun.java index 3eafc2d693..8c972590fd 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ApplicationRun.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ApplicationRun.java @@ -23,8 +23,7 @@ import java.nio.ByteBuffer; import java.util.Collection; -import javax.validation.Valid; - +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.HasChipLocation; import uk.ac.manchester.spinnaker.machine.ValidP; import uk.ac.manchester.spinnaker.messages.model.AppID; @@ -41,7 +40,7 @@ * Calls {@code proc_start_app()} in {@code scamp-app.c}. */ @UsedInJavadocOnly(SystemVariableDefinition.class) -public class ApplicationRun extends SCPRequest { +public final class ApplicationRun extends SCPRequest { private static final int WAIT_BIT = 18; /** @@ -90,7 +89,7 @@ private static int idOpMask(AppID appId, Collection processors, mask |= 1 << p; } } - mask |= appId.appID << BYTE3; + mask |= appId.appID() << BYTE3; if (wait) { mask |= 1 << WAIT_BIT; } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ApplicationStop.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ApplicationStop.java index 8c5522e459..aa5f23db98 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ApplicationStop.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ApplicationStop.java @@ -59,7 +59,7 @@ private static int data(AppID appID) { // Call the SIG0 NN operation, subcode STOP (stop application) // No masking of the app ID return (SIG0_APP << SHIFT) | (STOP.value << BYTE2) | (APP_MASK << BYTE1) - | (appID.appID << BYTE0); + | (appID.appID() << BYTE0); } private static int nnConfig() { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ClearIOBUF.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ClearIOBUF.java index 8418eff510..dce9f047ff 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ClearIOBUF.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ClearIOBUF.java @@ -28,7 +28,7 @@ * This calls {@code sark_io_buf_reset()} in {@code sark_io.c} (via * {@code simulation_control_scp_callback()} in {@code simulation.c}). */ -public class ClearIOBUF extends FECRequest { +public final class ClearIOBUF extends FECRequest { /** * @param core * The core to clear the IOBUF of. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ClearReinjectionQueue.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ClearReinjectionQueue.java index 9e8336f851..8ca13bdbd3 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ClearReinjectionQueue.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ClearReinjectionQueue.java @@ -28,7 +28,8 @@ *

* Handled by {@code reinjection_clear()} in {@code extra_monitor_support.c}. */ -public class ClearReinjectionQueue extends ReinjectorRequest { +public final class ClearReinjectionQueue + extends ReinjectorRequest { /** * @param core * The coordinates of the monitor core. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/CountState.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/CountState.java index 4a459d91f9..20ecb3c95f 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/CountState.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/CountState.java @@ -37,7 +37,7 @@ * {@code p2p_region()} in {@code scamp-cmd.c}. This is the main use of * point-to-point signals (and the only one exposed to users). */ -public class CountState extends SCPRequest { +public final class CountState extends SCPRequest { /* enum send_reg_ctrl */ private static final int APP_STAT = 1; @@ -66,7 +66,7 @@ public CountState(AppID appID, CPUState state) { */ // @formatter:on private static int data(AppID appId, CPUState state) { - int data = (APP_MASK << BYTE1) | (appId.appID << BYTE0); + int data = (APP_MASK << BYTE1) | (appId.appID() << BYTE0); data |= APP_STAT << OP_SHIFT; data |= MODE_SUM << MODE_SHIFT; data |= state.value << BYTE2; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/EmptyResponse.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/EmptyResponse.java index 489692822e..d484155a8e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/EmptyResponse.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/EmptyResponse.java @@ -22,7 +22,7 @@ /** * An SCP response to a request which has an empty payload. */ -public class EmptyResponse extends CheckOKResponse { +public final class EmptyResponse extends CheckOKResponse { /** * Create an instance. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FECRequest.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FECRequest.java index 4e0ab3a479..a10a8a5581 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FECRequest.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FECRequest.java @@ -15,10 +15,10 @@ */ package uk.ac.manchester.spinnaker.messages.scp; -import static uk.ac.manchester.spinnaker.messages.Utils.wordAsBuffer; import static uk.ac.manchester.spinnaker.messages.sdp.SDPHeader.Flag.REPLY_EXPECTED; import static uk.ac.manchester.spinnaker.messages.sdp.SDPHeader.Flag.REPLY_NOT_EXPECTED; import static uk.ac.manchester.spinnaker.messages.sdp.SDPPort.RUNNING_COMMAND_SDP_PORT; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.wordAsBuffer; import uk.ac.manchester.spinnaker.machine.HasCoreLocation; import uk.ac.manchester.spinnaker.messages.sdp.SDPHeader; @@ -31,8 +31,9 @@ * @param * The type of response expected. */ -// TODO Seal in 17 -public abstract class FECRequest extends SCPRequest { +public abstract sealed class FECRequest + extends SCPRequest< + T> permits ClearIOBUF, UpdateProvenanceAndExit, UpdateRuntime { /** * @param core * Where to send the request. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FillRequest.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FillRequest.java index 73e018c3a2..6bfcdd4182 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FillRequest.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FillRequest.java @@ -45,7 +45,7 @@ public final class FillRequest extends SCPRequest { */ public FillRequest(HasChipLocation chip, MemoryLocation baseAddress, int data, int size) { - super(chip.getScampCore(), CMD_FILL, baseAddress.address, data, size); + super(chip.getScampCore(), CMD_FILL, baseAddress.address(), data, size); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FixedRouteInitialise.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FixedRouteInitialise.java index 9fadd5c37a..43520614e2 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FixedRouteInitialise.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FixedRouteInitialise.java @@ -35,7 +35,7 @@ */ public final class FixedRouteInitialise extends SCPRequest { private static int argument1(AppID appID) { - return (appID.appID << BYTE1) | (FIXED.value << BYTE0); + return (appID.appID() << BYTE1) | (FIXED.value << BYTE0); } /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FixedRouteRead.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FixedRouteRead.java index 5bda659bdf..6233d53525 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FixedRouteRead.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FixedRouteRead.java @@ -37,7 +37,7 @@ */ public final class FixedRouteRead extends SCPRequest { private static int argument1(AppID appID) { - return (appID.appID << BYTE1) | (FIXED.value << BYTE0); + return (appID.appID() << BYTE1) | (FIXED.value << BYTE0); } // Top bit set = do a read diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FloodFillData.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FloodFillData.java index b6d007a049..ddc42e22e5 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FloodFillData.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FloodFillData.java @@ -41,7 +41,7 @@ * @see FloodFillStart * @see FloodFillEnd */ -public class FloodFillData extends SCPRequest { +public final class FloodFillData extends SCPRequest { private static final int FFD_NNP_FORWARD_RETRY = (FORWARD_LINKS << BYTE3) | ((DELAY | DATA_RESEND) << BYTE2); @@ -80,7 +80,7 @@ public FloodFillData(byte nearestNeighbourID, int blockNumber, public FloodFillData(byte nearestNeighbourID, int blockNumber, MemoryLocation baseAddress, byte[] data, int offset, int length) { super(BOOT_MONITOR_CORE, CMD_FFD, idFwdRty(nearestNeighbourID), - keyBase(blockNumber, length), baseAddress.address, + keyBase(blockNumber, length), baseAddress.address(), wrap(data, offset, length)); } @@ -100,7 +100,7 @@ public FloodFillData(byte nearestNeighbourID, int blockNumber, public FloodFillData(byte nearestNeighbourID, int blockNumber, MemoryLocation baseAddress, ByteBuffer data) { super(BOOT_MONITOR_CORE, CMD_FFD, idFwdRty(nearestNeighbourID), - keyBase(blockNumber, data.remaining()), baseAddress.address, + keyBase(blockNumber, data.remaining()), baseAddress.address(), data); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FloodFillEnd.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FloodFillEnd.java index 5aa03a2e84..0f06a77d92 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FloodFillEnd.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/FloodFillEnd.java @@ -98,7 +98,7 @@ private static int data(AppID appID, Iterable processors, processorMask |= 1 << p; } } - processorMask |= appID.appID << BYTE3; + processorMask |= appID.appID() << BYTE3; if (wait) { processorMask |= 1 << WAIT_BIT; } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetChipInfo.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetChipInfo.java index ccaa7a7422..3fbe769519 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetChipInfo.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetChipInfo.java @@ -29,7 +29,7 @@ *

* Calls {@code cmd_info()} in {@code scamp-cmd.c}. */ -public class GetChipInfo extends SCPRequest { +public final class GetChipInfo extends SCPRequest { private static final int FLAGS = 0x5F; private static final int SIZE_FLAG = 0x20; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetReinjectionStatus.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetReinjectionStatus.java index c94c461a3e..240791c92f 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetReinjectionStatus.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetReinjectionStatus.java @@ -30,7 +30,7 @@ * Handled by {@code reinjection_get_status()} in * {@code extra_monitor_support.c}. */ -public class GetReinjectionStatus +public final class GetReinjectionStatus extends ReinjectorRequest { /** * @param core diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetVersion.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetVersion.java index 208245a196..bf0e99f974 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetVersion.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/GetVersion.java @@ -30,7 +30,7 @@ * Calls {@code cmd_ver()} in {@code scamp-cmd.c} or {@code sark_cmd_ver()} in * {@code sark_base.c}, depending on which core the message is sent to. */ -public class GetVersion extends SCPRequest { +public final class GetVersion extends SCPRequest { /** * @param core * The location of the core to read from. @@ -46,6 +46,7 @@ public Response getSCPResponse(ByteBuffer buffer) } /** An SCP response to a request for the version of software running. */ + // Used in TestUDPConnection public static final class Response extends PayloadedResponse { Response(ByteBuffer buffer) throws UnexpectedResponseCodeException { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagClear.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagClear.java index e5f4163383..bfa7d0ce1e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagClear.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagClear.java @@ -32,7 +32,7 @@ * Handled by {@code cmd_iptag()} in {@code scamp-cmd.c} (or {@code bmp_cmd.c}, * if sent to a BMP). */ -public class IPTagClear extends SCPRequest { +public final class IPTagClear extends SCPRequest { /** * @param chip * The chip to clear the tag on. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagGet.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagGet.java index ed60962d5c..ca6dd2187c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagGet.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagGet.java @@ -15,8 +15,11 @@ */ package uk.ac.manchester.spinnaker.messages.scp; +import static java.net.InetAddress.getByAddress; import static java.util.Objects.requireNonNull; import static uk.ac.manchester.spinnaker.messages.model.IPTagFieldDefinitions.COMMAND_FIELD; +import static uk.ac.manchester.spinnaker.messages.model.IPTagFieldDefinitions.CORE_MASK; +import static uk.ac.manchester.spinnaker.messages.model.IPTagFieldDefinitions.PORT_SHIFT; import static uk.ac.manchester.spinnaker.messages.model.IPTagFieldDefinitions.THREE_BITS_MASK; import static uk.ac.manchester.spinnaker.messages.scp.IPTagCommand.GET; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_IPTAG; @@ -25,8 +28,10 @@ import java.nio.ByteBuffer; import uk.ac.manchester.spinnaker.connections.SCPConnection; +import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.machine.HasChipLocation; import uk.ac.manchester.spinnaker.machine.tags.TagID; +import uk.ac.manchester.spinnaker.messages.model.IPTagTimeOutWaitTime; import uk.ac.manchester.spinnaker.messages.model.TagDescription; import uk.ac.manchester.spinnaker.messages.model.UnexpectedResponseCodeException; @@ -37,7 +42,7 @@ * Handled by {@code cmd_iptag()} in {@code scamp-cmd.c} (or {@code bmp_cmd.c}, * if sent to a BMP). */ -public class IPTagGet extends SCPRequest +public final class IPTagGet extends SCPRequest implements ConnectionAwareMessage { private final int tag; @@ -67,26 +72,52 @@ public void setConnection(SCPConnection connection) { @Override public Response getSCPResponse(ByteBuffer buffer) - throws UnexpectedResponseCodeException, UnknownHostException { + throws UnexpectedResponseCodeException { + requireNonNull(conn, + "can only parse a tag description after the message has " + + "been sent on a connection"); return new Response(buffer); } + private static final int IPV4_BYTES = 4; + + private static final int MAC_BYTES = 6; + /** An SCP response to a request for an IP tags. */ protected final class Response - extends PayloadedResponse { + extends PayloadedResponse { private Response(ByteBuffer buffer) - throws UnexpectedResponseCodeException, UnknownHostException { + throws UnexpectedResponseCodeException { super("Get IP Tag Info", CMD_IPTAG, buffer); } @Override - protected TagDescription parse(ByteBuffer buffer) - throws UnknownHostException { - requireNonNull(conn, - "can only describe a tag fully after the message has " - + "been sent on a connection"); - return new TagDescription(buffer, sdpHeader.getSource(), - conn.getRemoteIPAddress(), tag); + protected TagDescription parse(ByteBuffer buffer) { + var ipBytes = new byte[IPV4_BYTES]; + buffer.get(ipBytes); + + var macAddress = new byte[MAC_BYTES]; + buffer.get(macAddress); + + int port = Short.toUnsignedInt(buffer.getShort()); + var timeout = IPTagTimeOutWaitTime.get(buffer.getShort()); + short flags = buffer.getShort(); + int count = buffer.getInt(); + int rxPort = Short.toUnsignedInt(buffer.getShort()); + int y = Byte.toUnsignedInt(buffer.get()); + int x = Byte.toUnsignedInt(buffer.get()); + int pp = Byte.toUnsignedInt(buffer.get()); // processor+port + + try { + return new TagDescription(count, flags, getByAddress(ipBytes), + macAddress, port, rxPort, + new CoreLocation(x, y, pp & CORE_MASK), + (pp >>> PORT_SHIFT) & THREE_BITS_MASK, timeout, + sdpHeader.getSource().asChipLocation(), + conn.getRemoteIPAddress(), tag); + } catch (UnknownHostException unexpectedException) { + return null; + } } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagGetInfo.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagGetInfo.java index 3b14cf9ba2..1020fdbecb 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagGetInfo.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagGetInfo.java @@ -36,7 +36,7 @@ * * @see IPTagGet */ -public class IPTagGetInfo extends SCPRequest { +public final class IPTagGetInfo extends SCPRequest { private static final int IPTAG_MAX = 255; /** @@ -51,7 +51,7 @@ public IPTagGetInfo(HasChipLocation chip) { @Override public Response getSCPResponse(ByteBuffer buffer) throws UnexpectedResponseCodeException { - return new IPTagGetInfo.Response(buffer); + return new Response(buffer); } /** An SCP response to a request for information about IP tags. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagSet.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagSet.java index 91a40049df..cf4846a5c0 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagSet.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagSet.java @@ -30,12 +30,11 @@ import java.nio.ByteBuffer; import java.util.Arrays; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; - import org.slf4j.Logger; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import uk.ac.manchester.spinnaker.machine.HasChipLocation; import uk.ac.manchester.spinnaker.machine.tags.TagID; import uk.ac.manchester.spinnaker.messages.model.UnexpectedResponseCodeException; @@ -51,7 +50,7 @@ * * @see ReverseIPTagSet */ -public class IPTagSet extends SCPRequest { +public final class IPTagSet extends SCPRequest { private static final Logger log = getLogger(IPTagSet.class); private static final int INADDRSZ = 4; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagSetTTO.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagSetTTO.java index 40a9fd0a5d..18a28a426e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagSetTTO.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/IPTagSetTTO.java @@ -34,7 +34,7 @@ * Handled by {@code cmd_iptag()} in {@code scamp-cmd.c} (or {@code bmp_cmd.c}, * if sent to a BMP). */ -public class IPTagSetTTO extends SCPRequest { +public final class IPTagSetTTO extends SCPRequest { /** * @param chip * The chip to set the tag timout on. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/NoResponse.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/NoResponse.java index 7b060241d4..2a963f1558 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/NoResponse.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/NoResponse.java @@ -23,7 +23,7 @@ * An SCP response that should never be received because its request is * guaranteed to be a one-way request. */ -public class NoResponse extends SCPResponse { +public final class NoResponse extends SCPResponse { /** * Create an instance. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/PayloadedResponse.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/PayloadedResponse.java index 3982a16723..c845e437cb 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/PayloadedResponse.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/PayloadedResponse.java @@ -31,8 +31,13 @@ * @param * The type of exception thrown by the payload parser. */ -public abstract class PayloadedResponse - extends CheckOKResponse implements Supplier { +public abstract sealed class PayloadedResponse + extends CheckOKResponse implements Supplier + permits CountState.Response, FixedRouteRead.Response, + GetChipInfo.Response, GetReinjectionStatus.Response, + GetVersion.Response, IPTagGet.Response, IPTagGetInfo.Response, + IPTagSetTTO.Response, ReadLink.Response, ReadMemory.Response, + RouterAlloc.Response, SDRAMAlloc.Response, SDRAMDeAlloc.Response { private final T value; PayloadedResponse(String operation, Enum command, ByteBuffer buffer) diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReadLink.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReadLink.java index 56e6af1604..111a8ef756 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReadLink.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReadLink.java @@ -15,9 +15,9 @@ */ package uk.ac.manchester.spinnaker.messages.scp; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_LINK_READ; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import java.nio.ByteBuffer; @@ -34,7 +34,7 @@ *

* Calls {@code cmd_link_read()} in {@code scamp-cmd.c}. */ -public class ReadLink extends SCPRequest { +public final class ReadLink extends SCPRequest { private static int validate(int size) { if (size < 1 || size > UDP_MESSAGE_MAX_SIZE) { throw new IllegalArgumentException( @@ -55,7 +55,7 @@ private static int validate(int size) { */ public ReadLink(HasCoreLocation core, Direction link, MemoryLocation baseAddress, int size) { - super(core, CMD_LINK_READ, baseAddress.address, validate(size), + super(core, CMD_LINK_READ, baseAddress.address(), validate(size), link.id); } @@ -71,7 +71,7 @@ public ReadLink(HasCoreLocation core, Direction link, */ public ReadLink(HasChipLocation chip, Direction link, MemoryLocation baseAddress, int size) { - super(chip.getScampCore(), CMD_LINK_READ, baseAddress.address, + super(chip.getScampCore(), CMD_LINK_READ, baseAddress.address(), validate(size), link.id); } @@ -95,7 +95,7 @@ protected final class Response /** @return The data read, as a read-only little-endian buffer. */ @Override protected ByteBuffer parse(ByteBuffer buffer) { - return buffer.asReadOnlyBuffer().order(LITTLE_ENDIAN); + return readOnly(buffer); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReadMemory.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReadMemory.java index e9ffbfe7b8..15361ea313 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReadMemory.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReadMemory.java @@ -15,10 +15,10 @@ */ package uk.ac.manchester.spinnaker.messages.scp; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.messages.model.TransferUnit.efficientTransferUnit; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_READ; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import java.nio.ByteBuffer; @@ -33,7 +33,7 @@ *

* Calls {@code sark_cmd_read()} in {@code sark_base.c}. */ -public class ReadMemory extends SCPRequest { +public final class ReadMemory extends SCPRequest { private static int validate(int size) { if (size < 1 || size > UDP_MESSAGE_MAX_SIZE) { throw new IllegalArgumentException( @@ -51,7 +51,7 @@ private static int validate(int size) { * The number of bytes to read, between 1 and 256 */ public ReadMemory(HasCoreLocation core, MemoryLocation address, int size) { - super(core, CMD_READ, address.address, validate(size), + super(core, CMD_READ, address.address(), validate(size), efficientTransferUnit(address, size).value); } @@ -64,7 +64,7 @@ public ReadMemory(HasCoreLocation core, MemoryLocation address, int size) { * The number of bytes to read, between 1 and 256 */ public ReadMemory(HasChipLocation chip, MemoryLocation address, int size) { - super(chip.getScampCore(), CMD_READ, address.address, validate(size), + super(chip.getScampCore(), CMD_READ, address.address(), validate(size), efficientTransferUnit(address, size).value); } @@ -88,7 +88,7 @@ protected final class Response /** @return The data read, in a little-endian read-only buffer. */ @Override protected ByteBuffer parse(ByteBuffer buffer) { - return buffer.asReadOnlyBuffer().order(LITTLE_ENDIAN); + return readOnly(buffer); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReinjectorRequest.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReinjectorRequest.java index 014ccffa00..ad09cc6ce7 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReinjectorRequest.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReinjectorRequest.java @@ -31,9 +31,11 @@ * @param * The type of response. */ -//TODO Seal in 17 -public abstract class ReinjectorRequest - extends SCPRequest { +public abstract sealed class ReinjectorRequest + extends SCPRequest + permits ClearReinjectionQueue, GetReinjectionStatus, + ResetReinjectionCounters, SetReinjectionPacketTypes, + SetRouterEmergencyTimeout, SetRouterTimeout { /** * @param core * Where to send the request. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ResetReinjectionCounters.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ResetReinjectionCounters.java index 310f8ff5bb..ecdddc607e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ResetReinjectionCounters.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ResetReinjectionCounters.java @@ -29,7 +29,8 @@ * Handled by {@code reinjection_reset_counters()} in * {@code extra_monitor_support.c}. */ -public class ResetReinjectionCounters extends ReinjectorRequest { +public final class ResetReinjectionCounters + extends ReinjectorRequest { /** * @param core * The coordinates of the monitor core. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReverseIPTagSet.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReverseIPTagSet.java index e8e121af79..d34fc481de 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReverseIPTagSet.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/ReverseIPTagSet.java @@ -29,9 +29,8 @@ import java.nio.ByteBuffer; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.HasChipLocation; import uk.ac.manchester.spinnaker.machine.HasCoreLocation; import uk.ac.manchester.spinnaker.machine.tags.TagID; @@ -48,7 +47,7 @@ * * @see IPTagSet */ -public class ReverseIPTagSet extends SCPRequest { +public final class ReverseIPTagSet extends SCPRequest { /** * @param chip * The chip to set the tag on. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterAlloc.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterAlloc.java index 25867d9afa..f9d776af5e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterAlloc.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterAlloc.java @@ -35,7 +35,7 @@ * Calls {@code rtr_alloc_id()} in {@code sark_alloc.c} via {@code cmd_alloc()} * in {@code scamp-cmd.c}. */ -public class RouterAlloc extends SCPRequest { +public final class RouterAlloc extends SCPRequest { private final int numEntries; /** @@ -53,7 +53,7 @@ public RouterAlloc(HasChipLocation chip, AppID appID, int numEntries) { } private static int argument1(AppID appID) { - return (appID.appID << BYTE1) | (ALLOC_ROUTING.value << BYTE0); + return (appID.appID() << BYTE1) | (ALLOC_ROUTING.value << BYTE0); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterClear.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterClear.java index 6c2a52dbea..d79c818601 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterClear.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterClear.java @@ -29,7 +29,7 @@ * Calls {@code rtr_mc_init()} in {@code sark_hw.c}, via {@code rtr_cmd()} in * {@code scamp-cmd.c}. */ -public class RouterClear extends SCPRequest { +public final class RouterClear extends SCPRequest { /** * @param chip * The coordinates of the chip to clear the router of diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterInit.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterInit.java index 373dbef88e..b9c8673300 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterInit.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterInit.java @@ -34,7 +34,7 @@ * Ultimately handled by {@code rtr_mc_load()} in {@code sark_hw.c} (via * {@code cmd_rtr()} in {@code scamp-cmd.c}). */ -public class RouterInit extends SCPRequest { +public final class RouterInit extends SCPRequest { /** One reserved for SCAMP. */ private static final int MAX_ENTRIES = 1023; @@ -57,7 +57,7 @@ public class RouterInit extends SCPRequest { public RouterInit(HasChipLocation chip, int numEntries, MemoryLocation tableAddress, int baseIndex, AppID appID) { super(chip.getScampCore(), CMD_RTR, argument1(numEntries, appID), - tableAddress.address, baseIndex); + tableAddress.address(), baseIndex); if (baseIndex < 0) { throw new IllegalArgumentException( "baseIndex must not be negative"); @@ -72,7 +72,7 @@ private static int argument1(int numEntries, AppID appID) { throw new IllegalArgumentException( "numEntries must be no more than " + MAX_ENTRIES); } - return (numEntries << HALF1) | (appID.appID << BYTE1) + return (numEntries << HALF1) | (appID.appID() << BYTE1) | (LOAD.value << BYTE0); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterTableRequest.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterTableRequest.java index 4cc1a1b70c..aafc589df0 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterTableRequest.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/RouterTableRequest.java @@ -31,8 +31,9 @@ * A command message to an extra monitor control core to manipulate the router * table. */ -//TODO Seal in 17 -public abstract class RouterTableRequest extends SCPRequest { +public abstract sealed class RouterTableRequest + extends SCPRequest + permits LoadApplicationRoutes, LoadSystemRoutes, SaveApplicationRoutes { private final RouterTableCommand cmd; /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SCPResponse.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SCPResponse.java index cd76228627..6e93a3edc2 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SCPResponse.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SCPResponse.java @@ -46,7 +46,7 @@ protected SCPResponse(ByteBuffer buffer) { assert buffer.order() == LITTLE_ENDIAN : "buffer.order=" + buffer.order(); buffer.getShort(); // SKIP TWO PADDING BYTES - sdpHeader = new SDPHeader(buffer, false); + sdpHeader = new SDPHeader(buffer); result = SCPResult.get(buffer.getShort()); sequence = Short.toUnsignedInt(buffer.getShort()); } @@ -71,8 +71,7 @@ protected final void throwIfNotOK(String operation, Enum command) case RC_OK: return; case RC_ROUTE: - throw new UnroutableMessageException(operation, command, - sdpHeader); + throw new UnroutableMessageException(operation, command, sdpHeader); default: throw new UnexpectedResponseCodeException(operation, command, result); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SDRAMAlloc.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SDRAMAlloc.java index 518276ef86..28b3854c75 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SDRAMAlloc.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SDRAMAlloc.java @@ -37,7 +37,7 @@ * Calls {@code cmd_alloc()} (and hence {@code sark_xalloc()}) in * {@code scamp-cmd.c}. */ -public class SDRAMAlloc extends SCPRequest { +public final class SDRAMAlloc extends SCPRequest { private static final int MAX_SDRAM_TAG = 255; private static final int FLAG_TAG_RETRY = 4; @@ -87,7 +87,7 @@ public SDRAMAlloc(HasChipLocation chip, AppID appID, int size, int tag) { */ // @formatter:on private static int argument(AppID appID) { - return (FLAG_TAG_RETRY << BYTE2) | (appID.appID << BYTE1) + return (FLAG_TAG_RETRY << BYTE2) | (appID.appID() << BYTE1) | (ALLOC_SDRAM.value << BYTE0); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SDRAMDeAlloc.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SDRAMDeAlloc.java index 197d85423c..d2bee41d8c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SDRAMDeAlloc.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SDRAMDeAlloc.java @@ -36,7 +36,7 @@ * Calls {@code cmd_alloc()} (and hence {@code sark_xfree()} or * {@code sark_xfree_id()}) in {@code scamp-cmd.c}. */ -public class SDRAMDeAlloc extends SCPRequest { +public final class SDRAMDeAlloc extends SCPRequest { private final boolean readNumFreedBlocks; /** @@ -63,7 +63,7 @@ public SDRAMDeAlloc(HasChipLocation chip, AppID appID) { */ public SDRAMDeAlloc(HasChipLocation chip, MemoryLocation baseAddress) { super(chip.getScampCore(), CMD_ALLOC, FREE_SDRAM_BY_POINTER.value, - baseAddress.address); + baseAddress.address()); readNumFreedBlocks = false; } @@ -74,7 +74,7 @@ public SDRAMDeAlloc(HasChipLocation chip, MemoryLocation baseAddress) { */ // @formatter:on private static int argument1(AppID appID) { - return (appID.appID << BYTE1) | (FREE_SDRAM_BY_APP_ID.value << BYTE0); + return (appID.appID() << BYTE1) | (FREE_SDRAM_BY_APP_ID.value << BYTE0); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SendSignal.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SendSignal.java index 301239a03a..828388ac8f 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SendSignal.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SendSignal.java @@ -41,7 +41,7 @@ * * @see CountState */ -public class SendSignal extends SCPRequest { +public final class SendSignal extends SCPRequest { /** * @param appID * The ID of the application to signal (only for multicast @@ -62,7 +62,7 @@ public SendSignal(AppID appID, Signal signal) { // @formatter:on private static int data(AppID appID, Signal signal) { return (signal.value << BYTE2) | (APP_MASK << BYTE1) - | (appID.appID << BYTE0); + | (appID.appID() << BYTE0); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetLED.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetLED.java index ed24688dc4..5d8ef7f4db 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetLED.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetLED.java @@ -29,7 +29,7 @@ *

* Handled by {@code sark_led_set()} in {@code sark_hw.c}. */ -public class SetLED extends SCPRequest { +public final class SetLED extends SCPRequest { /** * @param core * The SpiNNaker core that will set the BMPSetLED diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetReinjectionPacketTypes.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetReinjectionPacketTypes.java index 4c48d45b7c..ca221ad5f9 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetReinjectionPacketTypes.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetReinjectionPacketTypes.java @@ -30,7 +30,7 @@ * Handled by {@code reinjection_set_packet_types()} in * {@code extra_monitor_support.c}. */ -public class SetReinjectionPacketTypes +public final class SetReinjectionPacketTypes extends ReinjectorRequest { /** * @param core diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetRouterEmergencyTimeout.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetRouterEmergencyTimeout.java index 9e6876508e..b76e71b3c0 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetRouterEmergencyTimeout.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetRouterEmergencyTimeout.java @@ -30,7 +30,7 @@ * Handled by {@code reinjection_set_emergency_timeout_sdp()} in * {@code extra_monitor_support.c}. */ -public class SetRouterEmergencyTimeout +public final class SetRouterEmergencyTimeout extends ReinjectorRequest { /** * @param core diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetRouterTimeout.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetRouterTimeout.java index 584d612c05..a27ef944db 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetRouterTimeout.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/SetRouterTimeout.java @@ -30,7 +30,7 @@ * Handled by {@code reinjection_set_timeout_sdp()} in * {@code extra_monitor_support.c}. */ -public class SetRouterTimeout extends ReinjectorRequest { +public final class SetRouterTimeout extends ReinjectorRequest { /** * @param core * The coordinates of the monitor core. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/UpdateProvenanceAndExit.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/UpdateProvenanceAndExit.java index 01ee80c0ae..dc601e37a5 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/UpdateProvenanceAndExit.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/UpdateProvenanceAndExit.java @@ -29,7 +29,7 @@ *

* This calls {@code simulation_control_scp_callback()} in {@code simulation.c}. */ -public class UpdateProvenanceAndExit extends FECRequest { +public final class UpdateProvenanceAndExit extends FECRequest { /** * @param core * The SpiNNaker core to update the provenance info of. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/UpdateRuntime.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/UpdateRuntime.java index 0b0e4ce278..69779ef380 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/UpdateRuntime.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/UpdateRuntime.java @@ -28,7 +28,7 @@ *

* This calls {@code simulation_control_scp_callback()} in {@code simulation.c}. */ -public class UpdateRuntime extends FECRequest { +public final class UpdateRuntime extends FECRequest { /** * @param core * The SpiNNaker core to update the runtime info of. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/WriteLink.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/WriteLink.java index 2e1dd1aedf..3dfe75273d 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/WriteLink.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/WriteLink.java @@ -30,7 +30,7 @@ *

* Calls {@code cmd_link_write()} in {@code scamp-cmd.c}. */ -public class WriteLink extends SCPRequest { +public final class WriteLink extends SCPRequest { /** * @param core * the core to write via @@ -46,7 +46,7 @@ public class WriteLink extends SCPRequest { */ public WriteLink(HasCoreLocation core, Direction link, MemoryLocation baseAddress, ByteBuffer data) { - super(core, CMD_LINK_WRITE, baseAddress.address, data.remaining(), + super(core, CMD_LINK_WRITE, baseAddress.address(), data.remaining(), link.id, data); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/WriteMemory.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/WriteMemory.java index 7b0c979608..f9a942d427 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/WriteMemory.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/scp/WriteMemory.java @@ -30,7 +30,7 @@ *

* Calls {@code sark_cmd_write()} in {@code sark_base.c}. */ -public class WriteMemory extends SCPRequest { +public final class WriteMemory extends SCPRequest { /** * @param core * the core to write via @@ -44,7 +44,7 @@ public class WriteMemory extends SCPRequest { */ public WriteMemory(HasCoreLocation core, MemoryLocation baseAddress, ByteBuffer data) { - super(core, CMD_WRITE, baseAddress.address, data.remaining(), + super(core, CMD_WRITE, baseAddress.address(), data.remaining(), efficientTransferUnit(baseAddress, data.remaining()).value, data); } @@ -62,7 +62,7 @@ public WriteMemory(HasCoreLocation core, MemoryLocation baseAddress, */ public WriteMemory(HasChipLocation chip, MemoryLocation baseAddress, ByteBuffer data) { - super(chip.getScampCore(), CMD_WRITE, baseAddress.address, + super(chip.getScampCore(), CMD_WRITE, baseAddress.address(), data.remaining(), efficientTransferUnit(baseAddress, data.remaining()).value, data); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPHeader.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPHeader.java index 4687a4c7b7..5d556a0de3 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPHeader.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPHeader.java @@ -21,7 +21,7 @@ import java.nio.ByteBuffer; import java.util.Map; -import javax.validation.Valid; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.messages.SerializableMessage; @@ -80,8 +80,7 @@ public SDPHeader() { * @throws IllegalArgumentException * if a bad SDP port is given */ - public SDPHeader(Flag flags, SDPLocation destination, - int destinationPort) { + public SDPHeader(Flag flags, SDPLocation destination, int destinationPort) { this.flags = flags; this.destination = destination; if (destinationPort < 0 || destinationPort > MAX_PORT) { @@ -116,10 +115,8 @@ public SDPHeader(Flag flags, SDPLocation destination, * * @param buffer * The buffer to read from. - * @param isBMP - * Whether we're really talking to a BMP. */ - public SDPHeader(ByteBuffer buffer, boolean isBMP) { + public SDPHeader(ByteBuffer buffer) { // Caller MUST have stripped the leading padding assert buffer.position() == 2 : "leading padding must be skipped"; flags = Flag.get(buffer.get()); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPLocation.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPLocation.java index d755626d98..687254b6ef 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPLocation.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPLocation.java @@ -26,7 +26,6 @@ * or a BMP location. As such it can be interpreted as either. */ public class SDPLocation implements HasCoreLocation, HasBMPLocation { - /** Shift for major in hash code. **/ private static final int MAJOR_SHIFT = 24; @@ -51,7 +50,8 @@ public class SDPLocation implements HasCoreLocation, HasBMPLocation { /** * Make an SDPLocation from a core location. * - * @param core The core location to use. + * @param core + * The core location to use. */ public SDPLocation(HasCoreLocation core) { major = core.getX(); @@ -75,11 +75,14 @@ public SDPLocation(HasBMPLocation bmp) { } /** - * Make an SDPLocation from components. + * Make an instance from components. * - * @param major The first part of the location (x or cabinet). - * @param minor The second part of the location (y or frame). - * @param detail The third part of the location (p or board). + * @param major + * The first part of the location (x or cabinet). + * @param minor + * The second part of the location (y or frame). + * @param detail + * The third part of the location (p or board). */ public SDPLocation(int major, int minor, int detail) { this.major = major; @@ -179,11 +182,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (!(obj instanceof SDPLocation)) { - return false; - } - SDPLocation sdp = (SDPLocation) obj; - return sdp.major == major && sdp.minor == minor && sdp.detail == detail; + return (obj instanceof SDPLocation sdp) && (sdp.major == major) + && (sdp.minor == minor) && (sdp.detail == detail); } private static class InvalidSDPHeaderUseException extends RuntimeException { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPMessage.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPMessage.java index 89009c9e25..cdddfa4d0b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPMessage.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/messages/sdp/SDPMessage.java @@ -17,7 +17,7 @@ import static java.nio.ByteBuffer.allocate; import static java.nio.ByteBuffer.wrap; -import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import java.nio.ByteBuffer; @@ -105,11 +105,9 @@ public SDPMessage(SDPHeader header, ByteBuffer data) { * * @param buffer * The buffer holding the message. - * @param isBMP - * Whether we're really talking to a BMP */ - public SDPMessage(ByteBuffer buffer, boolean isBMP) { - super(new SDPHeader(buffer, isBMP)); + public SDPMessage(ByteBuffer buffer) { + super(new SDPHeader(buffer)); databuf = buffer.duplicate(); } @@ -131,6 +129,6 @@ public final ByteBuffer getData() { } else { buffer = allocate(0); } - return buffer.asReadOnlyBuffer().order(LITTLE_ENDIAN); + return readOnly(buffer); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/Configuration.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/Configuration.java index e4e4a26324..fff69170f5 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/Configuration.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/Configuration.java @@ -42,10 +42,6 @@ import java.util.List; import java.util.Map; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - import org.apache.commons.configuration2.INIConfiguration; import org.apache.commons.configuration2.SubnodeConfiguration; import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; @@ -57,6 +53,9 @@ import org.apache.commons.configuration2.io.HomeDirectoryLocationStrategy; import org.apache.commons.configuration2.io.ProvidedURLLocationStrategy; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.utils.validation.TCPPort; /** A spalloc configuration loaded from a file. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/CreateJob.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/CreateJob.java index 86f9dc0845..e66cde83a7 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/CreateJob.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/CreateJob.java @@ -35,13 +35,12 @@ import java.util.List; import java.util.Map; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - import com.google.errorprone.annotations.CanIgnoreReturnValue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.machine.board.ValidTriadHeight; import uk.ac.manchester.spinnaker.machine.board.ValidTriadWidth; import uk.ac.manchester.spinnaker.machine.board.ValidTriadX; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocAPI.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocAPI.java index 0bc285f3eb..db0cb7e49d 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocAPI.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocAPI.java @@ -19,12 +19,11 @@ import java.util.List; import java.util.Map; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.machine.HasChipLocation; import uk.ac.manchester.spinnaker.machine.board.PhysicalCoords; import uk.ac.manchester.spinnaker.machine.board.TriadCoords; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocClient.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocClient.java index c57b705cda..ac9c111b34 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocClient.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocClient.java @@ -219,7 +219,7 @@ public Version version(Integer timeout) if (log.isDebugEnabled()) { log.debug("version result: {}", result); } - return new Version(result); + return Version.parse(result); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocConnection.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocConnection.java index 5a59b9508f..35bc5a9d86 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocConnection.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocConnection.java @@ -421,15 +421,13 @@ protected String call(Command command, Integer timeout) if (r == null) { continue; } - if (r instanceof ReturnResponse) { - // Success! - return ((ReturnResponse) r).getReturnValue(); - } else if (r instanceof ExceptionResponse) { - // Server error! - throw new SpallocServerException((ExceptionResponse) r); - } else if (r instanceof Notification) { + if (r instanceof ReturnResponse success) { + return success.getReturnValue(); + } else if (r instanceof ExceptionResponse serverError) { + throw new SpallocServerException(serverError); + } else if (r instanceof Notification notification) { // Got a notification, keep trying... - notifications.add((Notification) r); + notifications.add(notification); } else { throw new SpallocProtocolException( "bad response: " + r.getClass()); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocJob.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocJob.java index 2bd559ec01..18517cdef4 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocJob.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocJob.java @@ -694,9 +694,9 @@ public List getConnections() @Override public String getHostname() throws IOException, SpallocServerException, InterruptedException { - for (Connection c : getConnections()) { - if (c.getChip().onSameChipAs(ZERO_ZERO)) { - return c.getHostname(); + for (var c : getConnections()) { + if (c.chip().onSameChipAs(ZERO_ZERO)) { + return c.hostname(); } } return null; @@ -883,7 +883,7 @@ public void waitUntilReady(Integer timeout) switch (curState) { case READY: log.info("job:{} is now ready", id); - // Now in the ready state! + // Now in the ready state! Done successfully. return; case QUEUED: log.info("job:{} has been queued by the spalloc server", id); @@ -915,7 +915,7 @@ public BoardPhysicalCoordinates whereIs(HasChipLocation chip) throw new IllegalStateException( "received null instead of machine location"); } - return result.getPhysical(); + return result.physical(); } /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocJobAPI.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocJobAPI.java index b9b3db1826..c21b7aa7a5 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocJobAPI.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/SpallocJobAPI.java @@ -201,8 +201,8 @@ default TransceiverInterface getTransceiver() } String bootHost = null; for (var c : connInfo) { - if (c.getChip().equals(ZERO_ZERO)) { - bootHost = c.getHostname(); + if (c.chip().equals(ZERO_ZERO)) { + bootHost = c.hostname(); } } if (bootHost == null) { @@ -212,8 +212,8 @@ default TransceiverInterface getTransceiver() uk.ac.manchester.spinnaker.connections.model.Connection>(); connections.add(new BootConnection(getByName(bootHost), null)); for (var c : connInfo) { - connections.add( - new SCPConnection(c.getChip(), getByName(c.getHostname()))); + connections + .add(new SCPConnection(c.chip(), getByName(c.hostname()))); } return new Transceiver(ver, connections); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/JobDestroyedException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/JobDestroyedException.java index f1e827d5e2..6a76bc1f74 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/JobDestroyedException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/JobDestroyedException.java @@ -15,8 +15,11 @@ */ package uk.ac.manchester.spinnaker.spalloc.exceptions; +import java.io.Serial; + /** Thrown when the job was destroyed while waiting for it to become ready. */ public class JobDestroyedException extends Exception { + @Serial private static final long serialVersionUID = 6082560756316191208L; /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocProtocolException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocProtocolException.java index 9a62b7e3be..ffff533182 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocProtocolException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocProtocolException.java @@ -16,9 +16,11 @@ package uk.ac.manchester.spinnaker.spalloc.exceptions; import java.io.IOException; +import java.io.Serial; /** Thrown when a network-level problem occurs during protocol handling. */ public class SpallocProtocolException extends IOException { + @Serial private static final long serialVersionUID = -1591596793445886688L; /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocProtocolTimeoutException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocProtocolTimeoutException.java index 57229de5a6..c3fba9822a 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocProtocolTimeoutException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocProtocolTimeoutException.java @@ -16,9 +16,11 @@ package uk.ac.manchester.spinnaker.spalloc.exceptions; import java.io.IOException; +import java.io.Serial; /** Thrown upon a protocol-level timeout. */ public class SpallocProtocolTimeoutException extends IOException { + @Serial private static final long serialVersionUID = -3573271239107837119L; /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocServerException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocServerException.java index fa42468ae8..e6626e7600 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocServerException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocServerException.java @@ -15,6 +15,8 @@ */ package uk.ac.manchester.spinnaker.spalloc.exceptions; +import java.io.Serial; + import uk.ac.manchester.spinnaker.spalloc.messages.ExceptionResponse; /** @@ -22,6 +24,7 @@ * a message. */ public class SpallocServerException extends Exception { + @Serial private static final long serialVersionUID = 3865188016221866202L; /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocStateChangeTimeoutException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocStateChangeTimeoutException.java index bb1c480051..61485edfda 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocStateChangeTimeoutException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/exceptions/SpallocStateChangeTimeoutException.java @@ -15,8 +15,11 @@ */ package uk.ac.manchester.spinnaker.spalloc.exceptions; +import java.io.Serial; + /** Thrown when a state change takes too long to occur. */ public class SpallocStateChangeTimeoutException extends Exception { + @Serial private static final long serialVersionUID = 4879238794331037892L; /** Create an instance. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardCoordinates.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardCoordinates.java index d36501bd18..0c41656ef8 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardCoordinates.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardCoordinates.java @@ -32,42 +32,22 @@ /** * The logical coordinates of a board. This would be {@link TriadCoords} except * it has a different serialization form for backward-compatibility. + * + * @param x + * the X coordinate + * @param y + * the Y coordinate + * @param z + * the Z coordinate */ -@JsonPropertyOrder({ - "x", "y", "z" -}) +@JsonPropertyOrder({ "x", "y", "z" }) @JsonFormat(shape = ARRAY) @JsonAutoDetect(setterVisibility = NON_PRIVATE) @Immutable -public final class BoardCoordinates { - @ValidTriadX - private final int x; - - @ValidTriadY - private final int y; - - @ValidTriadZ - private final int z; - - /** - * Create with given coordinates. - * - * @param x - * the X coordinate - * @param y - * the Y coordinate - * @param z - * the Z coordinate - */ - public BoardCoordinates( - @JsonProperty(value = "x", defaultValue = "0") int x, - @JsonProperty(value = "y", defaultValue = "0") int y, - @JsonProperty(value = "z", defaultValue = "0") int z) { - this.x = x; - this.y = y; - this.z = z; - } - +public record BoardCoordinates( + @JsonProperty(value = "x", defaultValue = "0") @ValidTriadX int x, + @JsonProperty(value = "y", defaultValue = "0") @ValidTriadY int y, + @JsonProperty(value = "z", defaultValue = "0") @ValidTriadZ int z) { /** * Create with given coordinates. * @@ -75,38 +55,7 @@ public BoardCoordinates( * the coordinates in standard form */ public BoardCoordinates(TriadCoords triad) { - this.x = triad.x; - this.y = triad.y; - this.z = triad.z; - } - - /** @return the X coordinate */ - public int getX() { - return x; - } - - /** @return the Y coordinate */ - public int getY() { - return y; - } - - /** @return the Z coordinate */ - public int getZ() { - return z; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BoardCoordinates) { - var other = (BoardCoordinates) o; - return x == other.x && y == other.y && z == other.z; - } - return false; - } - - @Override - public int hashCode() { - return x * 1234567 + y * 56789 + z; + this(triad.x(), triad.y(), triad.z()); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardLink.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardLink.java index 8c82f5516a..66cba22512 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardLink.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardLink.java @@ -30,69 +30,20 @@ * A combination of x, y, z and a Link. * * @author Christian + * @param x + * The X coordinate + * @param y + * The Y coordinate + * @param z + * The Z coordinate + * @param link + * The link number */ -@JsonPropertyOrder({ - "x", "y", "z", "link" -}) +@JsonPropertyOrder({ "x", "y", "z", "link" }) @JsonFormat(shape = ARRAY) @Immutable -public class BoardLink { +public record BoardLink(@JsonProperty("x") @ValidTriadX int x, + @JsonProperty("y") @ValidTriadY int y, + @JsonProperty("z") @ValidTriadZ int z, @JsonProperty("link") int link) { // TODO verify format and meaning. - - @ValidTriadX - private final int x; - - @ValidTriadY - private final int y; - - @ValidTriadZ - private final int z; - - private final int link; - - /** - * @param x - * The X coordinate - * @param y - * The Y coordinate - * @param z - * The Z coordinate - * @param link - * The link number - */ - public BoardLink(@JsonProperty("x") int x, @JsonProperty("y") int y, - @JsonProperty("z") int z, @JsonProperty("link") int link) { - this.x = x; - this.y = y; - this.z = z; - this.link = link; - } - - /** - * @return the X coordinate - */ - public int getX() { - return x; - } - - /** - * @return the Y coordinate - */ - public int getY() { - return y; - } - - /** - * @return the Z coordinate - */ - public int getZ() { - return z; - } - - /** - * @return the link number - */ - public int getLink() { - return link; - } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardPhysicalCoordinates.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardPhysicalCoordinates.java index b86c958f26..eee6834c78 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardPhysicalCoordinates.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/BoardPhysicalCoordinates.java @@ -17,9 +17,6 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NON_PRIVATE; import static com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY; -import static java.util.Objects.isNull; - -import java.util.Objects; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonFormat; @@ -35,42 +32,25 @@ /** * The physical coordinates of a board. This would be {@link PhysicalCoords} * except it has a different serialization form for backward-compatibility. + * + * @param cabinet + * the cabinet ID + * @param frame + * the frame ID within the cabinet + * @param board + * the board ID within the frame */ -@JsonPropertyOrder({ - "cabinet", "frame", "board" -}) +@JsonPropertyOrder({ "cabinet", "frame", "board" }) @JsonFormat(shape = ARRAY) @JsonAutoDetect(setterVisibility = NON_PRIVATE) @Immutable -public class BoardPhysicalCoordinates { - @ValidCabinetNumber - private final int cabinet; - - @ValidFrameNumber - private final int frame; - - @ValidBoardNumber - private final Integer board; - - /** - * Create with given coordinates. - * - * @param cabinet - * the cabinet ID - * @param frame - * the frame ID within the cabinet - * @param board - * the board ID within the frame - */ - public BoardPhysicalCoordinates( - @JsonProperty(value = "cabinet", defaultValue = "0") int cabinet, - @JsonProperty(value = "frame", defaultValue = "0") int frame, - @JsonProperty(value = "board", defaultValue = "0") Integer board) { - this.cabinet = cabinet; - this.frame = frame; - this.board = board; - } - +public record BoardPhysicalCoordinates( + @JsonProperty(value = "cabinet", defaultValue = "0") // + @ValidCabinetNumber int cabinet, + @JsonProperty(value = "frame", defaultValue = "0") // + @ValidFrameNumber int frame, + @JsonProperty(value = "board", defaultValue = "0") // + @ValidBoardNumber Integer board) { /** * Create with given coordinates. * @@ -78,40 +58,7 @@ public BoardPhysicalCoordinates( * the coordinates in standard form. */ public BoardPhysicalCoordinates(PhysicalCoords coords) { - this.cabinet = coords.c; - this.frame = coords.f; - this.board = coords.b; - } - - /** @return the cabinet ID */ - public int getCabinet() { - return cabinet; - } - - /** @return the frame ID within the cabinet */ - public int getFrame() { - return frame; - } - - /** @return the board ID within the frame */ - public Integer getBoard() { - return board; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BoardPhysicalCoordinates) { - var other = (BoardPhysicalCoordinates) o; - return cabinet == other.cabinet && frame == other.frame - && Objects.equals(board, other.board); - } - return false; - } - - @Override - public int hashCode() { - return 9 * (cabinet * 1234567 + frame * 56789 - + (isNull(board) ? 0 : board)); + this(coords.c(), coords.f(), coords.b()); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Command.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Command.java index e66dc33c2f..b145b946de 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Command.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Command.java @@ -27,7 +27,15 @@ * @param * The type of arguments. */ -public abstract class Command { +public abstract sealed class Command // + permits CreateJobCommand, DestroyJobCommand, GetBoardAtPositionCommand, + GetBoardPositionCommand, GetJobMachineInfoCommand, GetJobStateCommand, + JobKeepAliveCommand, ListJobsCommand, ListMachinesCommand, + NoNotifyJobCommand, NoNotifyMachineCommand, NotifyJobCommand, + NotifyMachineCommand, PowerOffJobBoardsCommand, PowerOnJobBoardsCommand, + VersionCommand, WhereIsJobChipCommand, + WhereIsMachineBoardLogicalCommand, WhereIsMachineBoardPhysicalCommand, + WhereIsMachineChipCommand, CustomIntCommand, CustomStringCommand { private final String command; private final List args = new ArrayList<>(); @@ -54,7 +62,7 @@ protected final void addKwArg(String key, Object value) { */ @SafeVarargs protected final void addArg(A... values) { - for (final A value : values) { + for (var value : values) { args.add(value); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Connection.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Connection.java index 6dc4cf6ad8..03497e26bc 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Connection.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Connection.java @@ -18,10 +18,6 @@ import static com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY; import static java.util.Objects.isNull; -import java.util.Objects; - -import javax.validation.Valid; - import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -29,26 +25,25 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.Immutable; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.HasChipLocation; import uk.ac.manchester.spinnaker.utils.validation.IPAddress; /** * Describes a connection by its chip and hostname. + * + * @param chip + * The chip for the connection. + * @param hostname + * Where to connect to to talk to the chip. */ -@JsonPropertyOrder({ - "chip", "hostname" -}) +@JsonPropertyOrder({ "chip", "hostname" }) @JsonFormat(shape = ARRAY) @JsonDeserialize(builder = Connection.Builder.class) @Immutable -public final class Connection { - @Valid - private final ChipLocation chip; - - @IPAddress - private final String hostname; - +public record Connection(@Valid ChipLocation chip, + @IPAddress String hostname) { /** * Create. * @@ -58,33 +53,7 @@ public final class Connection { * the host */ public Connection(HasChipLocation chip, String hostname) { - this.chip = isNull(chip) ? null : chip.asChipLocation(); - this.hostname = hostname; - } - - /** @return The chip for the connection. */ - public ChipLocation getChip() { - return chip; - } - - /** @return Where to connect to to talk to the chip. */ - public String getHostname() { - return hostname; - } - - @Override - public boolean equals(Object other) { - if (other instanceof Connection) { - var c = (Connection) other; - return Objects.equals(chip, c.chip) - && Objects.equals(hostname, c.hostname); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(hostname, chip); + this(isNull(chip) ? null : chip.asChipLocation(), hostname); } @Override @@ -92,9 +61,7 @@ public String toString() { return "Connection(" + chip + "@" + hostname + ")"; } - @JsonPropertyOrder({ - "chip", "hostname" - }) + @JsonPropertyOrder({ "chip", "hostname" }) @JsonFormat(shape = ARRAY) @JsonPOJOBuilder static class Builder { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CreateJobCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CreateJobCommand.java index 33175bbfff..e949ff56a4 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CreateJobCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CreateJobCommand.java @@ -25,7 +25,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.create_job" * >Spalloc Server documentation */ -public class CreateJobCommand extends Command { +public final class CreateJobCommand extends Command { /** * Create a request to create a job. Short-hand form for most basic kind of * request. @@ -63,7 +63,7 @@ public CreateJobCommand(List args, Map kwargs) { throw new IllegalArgumentException( "owner must be specified for all jobs"); } - for (String key : kwargs.keySet()) { + for (var key : kwargs.keySet()) { addKwArg(key, kwargs.get(key)); } } diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ProxyAwareStorage.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CustomIntCommand.java similarity index 52% rename from SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ProxyAwareStorage.java rename to SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CustomIntCommand.java index a84649eb11..2e7f5a2edd 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ProxyAwareStorage.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CustomIntCommand.java @@ -13,23 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package uk.ac.manchester.spinnaker.storage; +package uk.ac.manchester.spinnaker.spalloc.messages; /** - * Storage interface that knows how to get information about the proxy system - * from the DB. - * - * @author Donal Fellows + * A custom command not part of the standard protocol. These are not + * guaranteed to be accepted by any spalloc service. */ -public interface ProxyAwareStorage extends DatabaseAPI { +public abstract non-sealed class CustomIntCommand extends Command { /** - * Get the proxy information from the database. + * Create a command. * - * @return The proxy information, or {@code null} if none defined. When - * there is no proxy, only direct connections to SpiNNaker are - * possible. - * @throws StorageException - * If anything goes wrong. + * @param name + * The name of the command. + * @param args + * The integer positional arguments. */ - ProxyInformation getProxyInformation() throws StorageException; + public CustomIntCommand(String name, int... args) { + super(name); + for (int arg : args) { + addArg(arg); + } + } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CustomStringCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CustomStringCommand.java new file mode 100644 index 0000000000..fdff6c81fd --- /dev/null +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/CustomStringCommand.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 The University of Manchester + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package uk.ac.manchester.spinnaker.spalloc.messages; + +/** + * A custom command not part of the standard protocol. These are not + * guaranteed to be accepted by any spalloc service. + */ +public abstract non-sealed class CustomStringCommand extends Command { + /** + * Create a command. + * + * @param name + * The name of the command. + * @param args + * The string positional arguments. + */ + public CustomStringCommand(String name, String... args) { + super(name); + for (var arg : args) { + addArg(arg); + } + } +} diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/DestroyJobCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/DestroyJobCommand.java index f3ace20842..33861f0943 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/DestroyJobCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/DestroyJobCommand.java @@ -22,7 +22,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.destroy_job" * >Spalloc Server documentation */ -public class DestroyJobCommand extends Command { +public final class DestroyJobCommand extends Command { /** * Make a request to destroy a job. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ExceptionResponse.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ExceptionResponse.java index 6e2075d703..1d03aaf701 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ExceptionResponse.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ExceptionResponse.java @@ -23,7 +23,7 @@ * A response to a request that indicates a failure. */ @JsonAutoDetect(setterVisibility = NON_PRIVATE) -public class ExceptionResponse implements Response { +public final class ExceptionResponse implements Response { private String exception; /** @return The exception message. Should not include a stack trace. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetBoardAtPositionCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetBoardAtPositionCommand.java index ec6e24b463..6c52e9c9a8 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetBoardAtPositionCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetBoardAtPositionCommand.java @@ -15,9 +15,8 @@ */ package uk.ac.manchester.spinnaker.spalloc.messages; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import uk.ac.manchester.spinnaker.machine.board.PhysicalCoords; /** @@ -28,7 +27,7 @@ * >Spalloc Server documentation * @see BoardCoordinates The basic result type associated with the request */ -public class GetBoardAtPositionCommand extends Command { +public final class GetBoardAtPositionCommand extends Command { /** * Create a request. * @@ -42,8 +41,8 @@ public GetBoardAtPositionCommand(@NotBlank String machine, super("get_board_at_position"); addKwArg("machine_name", machine); // The current spalloc server expects the param names x, y, z - addKwArg("x", coords.c); - addKwArg("y", coords.f); - addKwArg("z", coords.b); + addKwArg("x", coords.c()); + addKwArg("y", coords.f()); + addKwArg("z", coords.b()); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetBoardPositionCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetBoardPositionCommand.java index 4578a5eb17..28045a83d1 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetBoardPositionCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetBoardPositionCommand.java @@ -15,9 +15,8 @@ */ package uk.ac.manchester.spinnaker.spalloc.messages; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import uk.ac.manchester.spinnaker.machine.board.TriadCoords; /** @@ -29,7 +28,7 @@ * @see BoardPhysicalCoordinates The basic result type associated with the * request */ -public class GetBoardPositionCommand extends Command { +public final class GetBoardPositionCommand extends Command { /** * Create a request. * @@ -42,8 +41,8 @@ public GetBoardPositionCommand(@NotBlank String machine, @Valid TriadCoords coords) { super("get_board_position"); addKwArg("machine_name", machine); - addKwArg("x", coords.x); - addKwArg("y", coords.y); - addKwArg("z", coords.z); + addKwArg("x", coords.x()); + addKwArg("y", coords.y()); + addKwArg("z", coords.z()); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetJobMachineInfoCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetJobMachineInfoCommand.java index eeffa0f671..e42b01ff8b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetJobMachineInfoCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetJobMachineInfoCommand.java @@ -23,7 +23,7 @@ * >Spalloc Server documentation * @see JobMachineInfo The basic result type associated with the request */ -public class GetJobMachineInfoCommand extends Command { +public final class GetJobMachineInfoCommand extends Command { /** * Create a request to get information about a job's allocated machine. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetJobStateCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetJobStateCommand.java index 99a7eeeaac..f5cb7fca99 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetJobStateCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/GetJobStateCommand.java @@ -23,7 +23,7 @@ * >Spalloc Server documentation * @see JobState The basic result type associated with the request */ -public class GetJobStateCommand extends Command { +public final class GetJobStateCommand extends Command { /** * Create a request to get the state of a job. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobKeepAliveCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobKeepAliveCommand.java index 5ac2d6e39e..48a5796393 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobKeepAliveCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobKeepAliveCommand.java @@ -22,7 +22,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.job_keepalive" * >Spalloc Server documentation */ -public class JobKeepAliveCommand extends Command { +public final class JobKeepAliveCommand extends Command { /** * Create a request to keep a job alive. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobState.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobState.java index 4719130fad..80a8fc154c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobState.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobState.java @@ -15,12 +15,12 @@ */ package uk.ac.manchester.spinnaker.spalloc.messages; -import javax.validation.constraints.Positive; - import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.google.errorprone.annotations.Immutable; +import jakarta.validation.constraints.Positive; + /** * A description of the state of a job, in terms of its state, whether its * boards are powered, the advised keep-alive polling interval and the reason diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobsChangedNotification.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobsChangedNotification.java index 5ff08d2eb5..fd9e1fe0a2 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobsChangedNotification.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/JobsChangedNotification.java @@ -26,7 +26,7 @@ * A response that describes what jobs have changed state. */ @JsonAutoDetect(setterVisibility = NON_PRIVATE) -public class JobsChangedNotification implements Notification { +public final class JobsChangedNotification implements Notification { private List jobsChanged = List.of(); /** Create a notification response. */ @@ -58,11 +58,8 @@ void setJobsChanged(List jobsChanged) { @Override public boolean equals(Object other) { - if (!(other instanceof JobsChangedNotification)) { - return false; - } - return jobsChanged - .equals(((JobsChangedNotification) other).jobsChanged); + return (other instanceof JobsChangedNotification jcn) + && jobsChanged.equals(jcn.jobsChanged); } @Override diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ListJobsCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ListJobsCommand.java index fa9d27bf57..8200f11e19 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ListJobsCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ListJobsCommand.java @@ -23,7 +23,7 @@ * >Spalloc Server documentation * @see JobDescription The basic result type associated with the request */ -public class ListJobsCommand extends Command { +public final class ListJobsCommand extends Command { /** * Create a request to list the jobs. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ListMachinesCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ListMachinesCommand.java index 880eeff505..0c85c59ef6 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ListMachinesCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ListMachinesCommand.java @@ -23,7 +23,7 @@ * >Spalloc Server documentation * @see Machine The basic result type associated with the request */ -public class ListMachinesCommand extends Command { +public final class ListMachinesCommand extends Command { /** Create a request to list the known SpiNNaker machines. */ public ListMachinesCommand() { super("list_machines"); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/MachinesChangedNotification.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/MachinesChangedNotification.java index a5aea3add7..3320787975 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/MachinesChangedNotification.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/MachinesChangedNotification.java @@ -26,7 +26,7 @@ * A response that describes what machines have changed state. */ @JsonAutoDetect(setterVisibility = NON_PRIVATE) -public class MachinesChangedNotification implements Notification { +public final class MachinesChangedNotification implements Notification { private List machinesChanged = List.of(); /** @return What machines have changed. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NoNotifyJobCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NoNotifyJobCommand.java index 8f488cbb9f..c48ed3c47f 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NoNotifyJobCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NoNotifyJobCommand.java @@ -22,7 +22,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.no_notify_job" * >Spalloc Server documentation */ -public class NoNotifyJobCommand extends Command { +public final class NoNotifyJobCommand extends Command { /** * Create a request to not be notified of changes in job state. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NoNotifyMachineCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NoNotifyMachineCommand.java index 6b690d3871..3bcb38781c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NoNotifyMachineCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NoNotifyMachineCommand.java @@ -22,7 +22,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.no_notify_machine" * >Spalloc Server documentation */ -public class NoNotifyMachineCommand extends Command { +public final class NoNotifyMachineCommand extends Command { // /** * Create a request to not be notified of changes in machine state. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Notification.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Notification.java index 247eee4680..2bb2429702 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Notification.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Notification.java @@ -19,6 +19,7 @@ * Notifications all implement. They are not sent in response to specific * requests unlike ordinary {@linkplain Response responses}. */ -public interface Notification extends Response { +public sealed interface Notification extends Response + permits JobsChangedNotification, MachinesChangedNotification { // empty } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NotifyJobCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NotifyJobCommand.java index b3c129274c..9b57bf6dfd 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NotifyJobCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NotifyJobCommand.java @@ -22,7 +22,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.notify_job" * >Spalloc Server documentation */ -public class NotifyJobCommand extends Command { +public final class NotifyJobCommand extends Command { /** * Create a request to be notified of changes in job state. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NotifyMachineCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NotifyMachineCommand.java index 5cfb28c57d..607292e4d7 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NotifyMachineCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/NotifyMachineCommand.java @@ -22,7 +22,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.notify_machine" * >Spalloc Server documentation */ -public class NotifyMachineCommand extends Command { +public final class NotifyMachineCommand extends Command { /** * Create a request to be notified of changes in machine state. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/PowerOffJobBoardsCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/PowerOffJobBoardsCommand.java index 3134443d6a..dda12e1d9e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/PowerOffJobBoardsCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/PowerOffJobBoardsCommand.java @@ -22,7 +22,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.power_off_job_boards" * >Spalloc Server documentation */ -public class PowerOffJobBoardsCommand extends Command { +public final class PowerOffJobBoardsCommand extends Command { /** * Create a request to turn off a job's allocated boards. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/PowerOnJobBoardsCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/PowerOnJobBoardsCommand.java index cbdb721f9c..5e7a8ace95 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/PowerOnJobBoardsCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/PowerOnJobBoardsCommand.java @@ -22,7 +22,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.power_on_job_boards" * >Spalloc Server documentation */ -public class PowerOnJobBoardsCommand extends Command { +public final class PowerOnJobBoardsCommand extends Command { /** * Create a request to turn on a job's allocated boards. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Response.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Response.java index f0b7ac479e..82662777f8 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Response.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/Response.java @@ -18,6 +18,7 @@ /** * An (abstract) response from the machine manager. Responses are all POJOs. */ -public interface Response { +public sealed interface Response + permits ExceptionResponse, Notification, ReturnResponse { // Does Nothing } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ReturnResponse.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ReturnResponse.java index d07e2e0607..be0e4cdf02 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ReturnResponse.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/ReturnResponse.java @@ -21,7 +21,7 @@ /** * A response that is the successful result of a request. */ -public class ReturnResponse implements Response { +public final class ReturnResponse implements Response { private String returnValue; /** @return The returned value, as a string. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/VersionCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/VersionCommand.java index 0a0e066c9d..83aebee7f7 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/VersionCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/VersionCommand.java @@ -22,7 +22,7 @@ * "https://spalloc-server.readthedocs.io/en/stable/protocol/#commands.version" * >Spalloc Server documentation */ -public class VersionCommand extends Command { +public final class VersionCommand extends Command { /** * Create a request to get the version of the spalloc server. */ diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIs.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIs.java index a442bee2ba..77b6da4aba 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIs.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIs.java @@ -15,8 +15,6 @@ */ package uk.ac.manchester.spinnaker.spalloc.messages; -import java.util.Objects; - import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.google.errorprone.annotations.Immutable; @@ -25,137 +23,27 @@ /** * The description of where some resource is on a SpiNNaker system. + * + * @param jobChip + * the chip location relative to the job's allocation. + * @param jobId + * the job id. + * @param chip + * the absolute chip location. + * @param logical + * the logical coordinates of the board + * @param machine + * the name of the machine + * @param boardChip + * the chip location relative to its board + * @param physical + * the physical coordinates of the board */ @Immutable @JsonDeserialize(builder = WhereIs.Builder.class) -public final class WhereIs { - private final ChipLocation jobChip; - - private final Integer jobId; - - private final ChipLocation chip; - - private final BoardCoordinates logical; - - private final String machine; - - private final ChipLocation boardChip; - - private final BoardPhysicalCoordinates physical; - - /** - * Create. - * - * @param jobChip - * the chip location relative to the job's allocation. - * @param jobId - * the job id. - * @param chip - * the absolute chip location. - * @param logical - * the logical coordinates of the board - * @param machine - * the name of the machine - * @param boardChip - * the chip location relative to its board - * @param physical - * the physical coordinates of the board - */ - public WhereIs(ChipLocation jobChip, Integer jobId, ChipLocation chip, - BoardCoordinates logical, String machine, ChipLocation boardChip, - BoardPhysicalCoordinates physical) { - this.jobChip = jobChip; - this.jobId = jobId; - this.chip = chip; - this.logical = logical; - this.machine = machine; - this.boardChip = boardChip; - this.physical = physical; - } - - /** - * Get the chip location relative to the job's allocation. - * - * @return the job-relative chip location - */ - public ChipLocation getJobChip() { - return jobChip; - } - - /** - * Get the job id. - * - * @return the job id - */ - public Integer getJobId() { - return jobId; - } - - /** - * Get the chip. - * - * @return the chip - */ - public ChipLocation getChip() { - return chip; - } - - /** - * Get the logical board coordinates. - * - * @return the logical board coordinates - */ - public BoardCoordinates getLogical() { - return logical; - } - - /** - * Get the machine. - * - * @return the machine - */ - public String getMachine() { - return machine; - } - - /** - * Get the chip location relative to the board. - * - * @return the board chip location - */ - public ChipLocation getBoardChip() { - return boardChip; - } - - /** - * Get the physical board coordinates. - * - * @return the physical board coordinates - */ - public BoardPhysicalCoordinates getPhysical() { - return physical; - } - - @Override - public boolean equals(Object o) { - if (o instanceof WhereIs) { - var other = (WhereIs) o; - return Objects.equals(jobChip, other.jobChip) - && Objects.equals(jobId, other.jobId) - && Objects.equals(chip, other.chip) - && Objects.equals(logical, other.logical) - && Objects.equals(machine, other.machine) - && Objects.equals(boardChip, other.boardChip) - && Objects.equals(physical, other.physical); - } - return false; - } - - @Override - public int hashCode() { - throw new UnsupportedOperationException(); - } - +public record WhereIs(ChipLocation jobChip, Integer jobId, ChipLocation chip, + BoardCoordinates logical, String machine, ChipLocation boardChip, + BoardPhysicalCoordinates physical) { @Override public String toString() { return "jobChip: " + jobChip + " jobId: " + jobId + " chip: " + chip diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsJobChipCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsJobChipCommand.java index aa36829533..525ed9d208 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsJobChipCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsJobChipCommand.java @@ -26,7 +26,7 @@ * >Spalloc Server documentation * @see WhereIs The basic result type associated with the request */ -public class WhereIsJobChipCommand extends Command { +public final class WhereIsJobChipCommand extends Command { /** * Create a request to locate a chip within a job's allocation. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineBoardLogicalCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineBoardLogicalCommand.java index d6a39a45de..2daabfab78 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineBoardLogicalCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineBoardLogicalCommand.java @@ -15,9 +15,8 @@ */ package uk.ac.manchester.spinnaker.spalloc.messages; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; import uk.ac.manchester.spinnaker.machine.board.TriadCoords; /** @@ -28,7 +27,7 @@ * >Spalloc Server documentation * @see WhereIs The basic result type associated with the request */ -public class WhereIsMachineBoardLogicalCommand extends Command { +public final class WhereIsMachineBoardLogicalCommand extends Command { /** * Create a request to locate a board on a machine. * @@ -41,8 +40,8 @@ public WhereIsMachineBoardLogicalCommand(@NotBlank String machine, @Valid TriadCoords coords) { super("where_is"); addKwArg("machine", machine); - addKwArg("x", coords.x); - addKwArg("y", coords.y); - addKwArg("z", coords.z); + addKwArg("x", coords.x()); + addKwArg("y", coords.y()); + addKwArg("z", coords.z()); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineBoardPhysicalCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineBoardPhysicalCommand.java index 8cdd9094d8..ad0bde4610 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineBoardPhysicalCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineBoardPhysicalCommand.java @@ -15,10 +15,9 @@ */ package uk.ac.manchester.spinnaker.spalloc.messages; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.board.PhysicalCoords; /** @@ -29,7 +28,7 @@ * >Spalloc Server documentation * @see WhereIs The basic result type associated with the request */ -public class WhereIsMachineBoardPhysicalCommand extends Command { +public final class WhereIsMachineBoardPhysicalCommand extends Command { /** * Create a request to locate a board on a machine. * @@ -42,8 +41,8 @@ public WhereIsMachineBoardPhysicalCommand(@NotBlank String machine, @Valid @NotNull PhysicalCoords coords) { super("where_is"); addKwArg("machine", machine); - addKwArg("cabinet", coords.c); - addKwArg("frame", coords.f); - addKwArg("board", coords.b); + addKwArg("cabinet", coords.c()); + addKwArg("frame", coords.f()); + addKwArg("board", coords.b()); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineChipCommand.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineChipCommand.java index c5310bb1fe..eda962357e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineChipCommand.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/spalloc/messages/WhereIsMachineChipCommand.java @@ -25,7 +25,7 @@ * >Spalloc Server documentation * @see WhereIs The basic result type associated with the request */ -public class WhereIsMachineChipCommand extends Command { +public final class WhereIsMachineChipCommand extends Command { /** * Create a request to locate a chip on a machine. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Accumulator.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Accumulator.java index 680c14dd70..77d7a71e66 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Accumulator.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Accumulator.java @@ -18,7 +18,7 @@ import static java.lang.Math.max; import static java.nio.ByteBuffer.allocate; import static java.nio.ByteBuffer.wrap; -import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; import java.io.IOException; import java.io.RandomAccessFile; @@ -109,7 +109,7 @@ public ByteBuffer finish() { done = true; buffer.limit(maxpos); } - return buffer.asReadOnlyBuffer().order(LITTLE_ENDIAN); + return readOnly(buffer); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/AppIdTracker.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/AppIdTracker.java index a8c51c664d..0dac612d7c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/AppIdTracker.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/AppIdTracker.java @@ -125,7 +125,7 @@ public void allocateID(AppID id) { * if the ID is out of range */ public void freeID(AppID id) { - if (id.appID < minID || id.appID > maxID) { + if (id.appID() < minID || id.appID() > maxID) { throw new IllegalArgumentException( "ID " + id + " out of allowed range of " + minID + " to " + maxID); diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ApplicationRunProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ApplicationRunProcess.java index 49ffeb1364..0fd993d01a 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ApplicationRunProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ApplicationRunProcess.java @@ -24,7 +24,7 @@ import uk.ac.manchester.spinnaker.messages.scp.ApplicationRun; /** Launch an application. */ -class ApplicationRunProcess extends TxrxProcess { +final class ApplicationRunProcess extends TxrxProcess { /** * Create. * diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPReadMemoryProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPReadMemoryProcess.java index 4a1959f368..05ba5f9cbb 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPReadMemoryProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPReadMemoryProcess.java @@ -27,7 +27,7 @@ import uk.ac.manchester.spinnaker.connections.ConnectionSelector; import uk.ac.manchester.spinnaker.machine.MemoryLocation; import uk.ac.manchester.spinnaker.machine.board.BMPBoard; -import uk.ac.manchester.spinnaker.messages.bmp.BMPReadMemory; +import uk.ac.manchester.spinnaker.messages.bmp.ReadBMPMemory; import uk.ac.manchester.spinnaker.transceiver.Accumulator.BufferAccumulator; import uk.ac.manchester.spinnaker.transceiver.Accumulator.FileAccumulator; @@ -73,7 +73,7 @@ private T read(BMPBoard board, MemoryLocation address, int size, for (int offset = 0, chunk; offset < size; offset += chunk) { chunk = min(size - offset, UDP_MESSAGE_MAX_SIZE); accum.add(offset, - call(new BMPReadMemory(board, address.add(offset), chunk))); + call(new ReadBMPMemory(board, address.add(offset), chunk))); } return accum.finish(); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPSendFailedException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPSendFailedException.java index 0592f31b60..857ec0e75a 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPSendFailedException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPSendFailedException.java @@ -19,6 +19,7 @@ import static uk.ac.manchester.spinnaker.transceiver.BMPCommandProcess.BMP_RETRIES; import java.io.IOException; +import java.io.Serial; import java.util.List; import uk.ac.manchester.spinnaker.machine.board.HasBMPLocation; @@ -28,6 +29,7 @@ * Indicates that message sending to a BMP failed for various reasons. */ public final class BMPSendFailedException extends IOException { + @Serial private static final long serialVersionUID = -7806549580351626377L; BMPSendFailedException(SCPRequest req, HasBMPLocation bmp, diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPSendTimedOutException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPSendTimedOutException.java index 5183636bc9..985d0311bd 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPSendTimedOutException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPSendTimedOutException.java @@ -18,6 +18,7 @@ import static java.lang.String.format; import static uk.ac.manchester.spinnaker.utils.UnitConstants.MSEC_PER_SEC; +import java.io.Serial; import java.net.SocketTimeoutException; import uk.ac.manchester.spinnaker.messages.scp.SCPRequest; @@ -27,6 +28,7 @@ */ public final class BMPSendTimedOutException extends SocketTimeoutException { + @Serial private static final long serialVersionUID = 1660563278795501381L; BMPSendTimedOutException(SCPRequest req, int timeout) { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPTransceiverInterface.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPTransceiverInterface.java index b309136109..ca6e821c26 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPTransceiverInterface.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPTransceiverInterface.java @@ -16,11 +16,8 @@ package uk.ac.manchester.spinnaker.transceiver; import static java.lang.Thread.interrupted; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.machine.MemoryLocation.NULL; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; -import static uk.ac.manchester.spinnaker.messages.Utils.wordAsBuffer; import static uk.ac.manchester.spinnaker.messages.model.PowerCommand.POWER_OFF; import static uk.ac.manchester.spinnaker.messages.model.PowerCommand.POWER_ON; import static uk.ac.manchester.spinnaker.transceiver.BMPConstants.BLACKLIST_BLANK; @@ -32,6 +29,8 @@ import static uk.ac.manchester.spinnaker.transceiver.BMPConstants.SF_BL_LEN; import static uk.ac.manchester.spinnaker.transceiver.Utils.crc; import static uk.ac.manchester.spinnaker.transceiver.Utils.fill; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.wordAsBuffer; import java.io.File; import java.io.IOException; @@ -40,12 +39,11 @@ import java.util.Collection; import java.util.Set; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; - import com.google.errorprone.annotations.CheckReturnValue; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import uk.ac.manchester.spinnaker.machine.MemoryLocation; import uk.ac.manchester.spinnaker.machine.board.BMPBoard; import uk.ac.manchester.spinnaker.machine.board.BMPCoords; @@ -1520,8 +1518,7 @@ default void writeBlacklist(@Valid BMPCoords bmp, @Valid BMPBoard board, interrupted(); // Prepare the boot data - var data = allocate(BMP_BOOT_SECTOR_SIZE); - data.order(LITTLE_ENDIAN); + var data = alloc(BMP_BOOT_SECTOR_SIZE); data.put(readBMPMemory(bmp, board, BMP_BOOT_SECTOR_ADDR, BMP_BOOT_SECTOR_SIZE)); fill(data, BMP_BOOT_BLACKLIST_OFFSET, SF_BL_LEN, BLACKLIST_BLANK); @@ -1535,11 +1532,11 @@ default void writeBlacklist(@Valid BMPCoords bmp, @Valid BMPBoard board, } // Prepare the serial flash update; must read part of the data first - var sfData = new byte[SF_BL_ADDR.address + SF_BL_LEN]; - readSerialFlash(bmp, board, NULL, SF_BL_ADDR.address).get(sfData, 0, - SF_BL_ADDR.address); + var sfData = new byte[SF_BL_ADDR.address() + SF_BL_LEN]; + readSerialFlash(bmp, board, NULL, SF_BL_ADDR.address()).get(sfData, 0, + SF_BL_ADDR.address()); data.position(BMP_BOOT_BLACKLIST_OFFSET); - data.get(sfData, SF_BL_ADDR.address, SF_BL_LEN); + data.get(sfData, SF_BL_ADDR.address(), SF_BL_LEN); data.position(0); // Prep for write diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPWriteMemoryProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPWriteMemoryProcess.java index 75716b6b3d..b08ef2495d 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPWriteMemoryProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPWriteMemoryProcess.java @@ -17,9 +17,9 @@ import static java.lang.Math.min; import static java.nio.ByteBuffer.allocate; +import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.read; -import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.slice; import java.io.IOException; import java.io.InputStream; @@ -31,7 +31,7 @@ import uk.ac.manchester.spinnaker.machine.MemoryLocation; import uk.ac.manchester.spinnaker.machine.board.BMPBoard; import uk.ac.manchester.spinnaker.messages.Constants; -import uk.ac.manchester.spinnaker.messages.bmp.BMPWriteMemory; +import uk.ac.manchester.spinnaker.messages.bmp.WriteBMPMemory; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; import uk.ac.manchester.spinnaker.utils.ValueHolder; @@ -80,7 +80,7 @@ void writeMemory(BMPBoard board, MemoryLocation baseAddress, @Override ByteBuffer prepareSendBuffer(int chunkSize) { - var buffer = slice(data, offset, chunkSize); + var buffer = data.slice(offset, chunkSize).order(LITTLE_ENDIAN); offset += chunkSize; return buffer; } @@ -137,7 +137,7 @@ ByteBuffer prepareSendBuffer(int chunkSize) { * * @author Donal Fellows */ -abstract class BMPWriteIterator implements Iterable { +abstract class BMPWriteIterator implements Iterable { private final BMPBoard board; private int sizeRemaining; @@ -172,7 +172,7 @@ abstract class BMPWriteIterator implements Iterable { abstract ByteBuffer prepareSendBuffer(int plannedSize); @Override - public Iterator iterator() { + public Iterator iterator() { return new Iterator<>() { @Override public boolean hasNext() { @@ -185,10 +185,10 @@ public boolean hasNext() { } @Override - public BMPWriteMemory next() { + public WriteBMPMemory next() { int chunkSize = sendBuffer.remaining(); try { - return new BMPWriteMemory(board, address, sendBuffer); + return new WriteBMPMemory(board, address, sendBuffer); } finally { address = address.add(chunkSize); sizeRemaining -= chunkSize; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPWriteSerialFlashProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPWriteSerialFlashProcess.java index 2d87148ea7..4be5f05fd1 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPWriteSerialFlashProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/BMPWriteSerialFlashProcess.java @@ -17,9 +17,9 @@ import static java.lang.Math.min; import static java.nio.ByteBuffer.allocate; +import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.read; -import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.slice; import java.io.IOException; import java.io.InputStream; @@ -80,7 +80,7 @@ void write(BMPBoard board, MemoryLocation baseAddress, ByteBuffer data) @Override ByteBuffer prepareSendBuffer(int chunkSize) { - var buffer = slice(data, offset, chunkSize); + var buffer = data.slice(offset, chunkSize).order(LITTLE_ENDIAN); offset += chunkSize; return buffer; } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/CoresNotInStateException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/CoresNotInStateException.java index 7162b52340..140da9b4a5 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/CoresNotInStateException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/CoresNotInStateException.java @@ -19,6 +19,7 @@ import static java.lang.String.format; import static uk.ac.manchester.spinnaker.utils.UnitConstants.MSEC_PER_SEC; +import java.io.Serial; import java.util.EnumSet; import uk.ac.manchester.spinnaker.machine.CoreSubsets; @@ -30,6 +31,7 @@ * @author Donal Fellows */ public class CoresNotInStateException extends SpinnmanException { + @Serial private static final long serialVersionUID = 1790369744408178478L; private static final String OP_TMPL = diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FillDataType.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FillDataType.java index 986ba9fd44..3db9042886 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FillDataType.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FillDataType.java @@ -22,11 +22,26 @@ */ public enum FillDataType { /** Fill by words (4 bytes). */ - WORD(4), + WORD(4) { + @Override + public void writeTo(int value, ByteBuffer buffer) { + buffer.putInt(value); + } + }, /** Fill by half words (2 bytes). */ - HALF_WORD(2), + HALF_WORD(2) { + @Override + public void writeTo(int value, ByteBuffer buffer) { + buffer.putShort((short) value); + } + }, /** Fill by single bytes. */ - BYTE(1); + BYTE(1) { + @Override + public void writeTo(int value, ByteBuffer buffer) { + buffer.put((byte) value); + } + }; /** The encoding of the fill unit size. */ public final int size; @@ -43,22 +58,5 @@ public enum FillDataType { * @param buffer * The buffer to write to. */ - public void writeTo(int value, ByteBuffer buffer) { - switch (this) { - case WORD: - buffer.putInt(value); - break; - case HALF_WORD: - buffer.putShort((short) value); - break; - case BYTE: - buffer.put((byte) value); - break; - default: - // unreachable - // CHECKSTYLE:OFF - throw new IllegalStateException(); - // CHECKSTYLE:ON - } - } + public abstract void writeTo(int value, ByteBuffer buffer); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FillProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FillProcess.java index 50533e79e7..f3eec1c0b3 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FillProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FillProcess.java @@ -16,10 +16,9 @@ package uk.ac.manchester.spinnaker.transceiver; import static java.lang.String.format; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.io.IOException; import java.nio.ByteBuffer; @@ -34,7 +33,7 @@ import uk.ac.manchester.spinnaker.messages.scp.WriteMemory; /** A process for filling memory. */ -class FillProcess extends TxrxProcess { +final class FillProcess extends TxrxProcess { private static final Logger log = getLogger(FillProcess.class); private static final int ALIGNMENT = 4; @@ -99,7 +98,7 @@ void fillMemory(HasChipLocation chip, MemoryLocation baseAddress, int data, } // Get a word of data regardless of the type - var buffer = allocate(TWO_WORDS).order(LITTLE_ENDIAN); + var buffer = alloc(TWO_WORDS); while (buffer.hasRemaining()) { dataType.writeTo(data, buffer); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FixedRouteControlProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FixedRouteControlProcess.java index 499125a744..e22f64982c 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FixedRouteControlProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/FixedRouteControlProcess.java @@ -28,7 +28,7 @@ import uk.ac.manchester.spinnaker.messages.scp.FixedRouteRead; /** Load a fixed route routing entry onto a chip, and read it back again. */ -class FixedRouteControlProcess extends TxrxProcess { +final class FixedRouteControlProcess extends TxrxProcess { /** * @param connectionSelector * How to select how to communicate. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetCPUInfoProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetCPUInfoProcess.java index d1a4033534..1e5ef61992 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetCPUInfoProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetCPUInfoProcess.java @@ -32,7 +32,7 @@ /** * Get the CPU information structure for a set of processors. */ -class GetCPUInfoProcess extends TxrxProcess { +final class GetCPUInfoProcess extends TxrxProcess { /** * @param connectionSelector * How to select how to communicate. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetHeapProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetHeapProcess.java index 1d3f553122..b7803a07eb 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetHeapProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetHeapProcess.java @@ -82,7 +82,7 @@ List getBlocks(HasChipLocation chip, var block = getBlockHeader(chip, nextBlock); if (!block.next.isNull()) { blocks.add(new HeapElement(nextBlock, block.next, - block.free.address)); + block.free.address())); } nextBlock = block.next; } @@ -119,7 +119,7 @@ List getFreeBlocks(HasChipLocation chip, var block = getBlockHeader(chip, nextBlock); if (!block.next.isNull()) { blocks.add(new HeapElement(nextBlock, block.next, - block.free.address)); + block.free.address())); } nextBlock = block.free; } @@ -219,39 +219,29 @@ private IntBuffer readFromAddress(HasChipLocation chip, } @SARKStruct("heap_t") - private static class HeapHeader { - @SARKField("free") - final MemoryLocation free; - - @SARKField("first") - final MemoryLocation first; - - @SARKField("last") - final MemoryLocation last; - - @SARKField("free_bytes") - final int freeBytes; - + private record HeapHeader(// + @SARKField("free") MemoryLocation free, + @SARKField("first") MemoryLocation first, + @SARKField("last") MemoryLocation last, + @SARKField("free_bytes") int freeBytes) { HeapHeader(IntBuffer data) { - free = new MemoryLocation(data.get()); - first = new MemoryLocation(data.get()); - last = new MemoryLocation(data.get()); - freeBytes = data.get(); + this(// + new MemoryLocation(data.get()), + new MemoryLocation(data.get()), + new MemoryLocation(data.get()), // + data.get()); // Note that we don't read or look at the 'buffer' field } } @SARKStruct("block_t") - private static class BlockHeader { - @SARKField("next") - final MemoryLocation next; - - @SARKField("free") - final MemoryLocation free; - + private record BlockHeader(// + @SARKField("next") MemoryLocation next, + @SARKField("free") MemoryLocation free) { BlockHeader(IntBuffer data) { - next = new MemoryLocation(data.get()); - free = new MemoryLocation(data.get()); + this(// + new MemoryLocation(data.get()), + new MemoryLocation(data.get())); } } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetMachineProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetMachineProcess.java index 9d31035c49..3e1a64257b 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetMachineProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetMachineProcess.java @@ -53,7 +53,7 @@ import uk.ac.manchester.spinnaker.messages.scp.ReadMemory; /** A process for getting the machine details over a set of connections. */ -class GetMachineProcess extends TxrxProcess { +final class GetMachineProcess extends TxrxProcess { private static final Logger log = getLogger(GetMachineProcess.class); /** A dictionary of (x, y) → ChipInfo. */ @@ -137,9 +137,9 @@ private static int clamp(int value, Integer limit) { Machine getMachineDetails(HasChipLocation bootChip, MachineDimensions size) throws IOException, ProcessException, InterruptedException { // Get the P2P table; 8 entries are packed into each 32-bit word - var p2pColumnData = new ByteBuffer[size.width]; - int byteLength = getNumColumnBytes(size.height); - for (int col : range(0, size.width)) { + var p2pColumnData = new ByteBuffer[size.width()]; + int byteLength = getNumColumnBytes(size.height()); + for (int col : range(0, size.width())) { sendGet(new ReadMemory(bootChip, ROUTER_P2P.add(getColumnOffset(col)), byteLength), bytes -> p2pColumnData[col] = bytes); @@ -251,8 +251,8 @@ private List makeLinks(ChipSummaryInfo chipInfo, private static ChipLocation getChipOverLink(HasChipLocation chip, MachineDimensions size, Direction link) { /// TODO CHECK negative wraparound! - int x = (chip.getX() + link.xChange + size.width) % size.width; - int y = (chip.getY() + link.yChange + size.height) % size.height; + int x = (chip.getX() + link.xChange + size.width()) % size.width(); + int y = (chip.getY() + link.yChange + size.height()) % size.height(); return new ChipLocation(x, y); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetTagsProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetTagsProcess.java index f11c41ee0e..2361671af5 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetTagsProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/GetTagsProcess.java @@ -29,7 +29,7 @@ import uk.ac.manchester.spinnaker.messages.scp.IPTagGetInfo; /** Gets IP tags and reverse IP tags. */ -class GetTagsProcess extends TxrxProcess { +final class GetTagsProcess extends TxrxProcess { /** * @param connectionSelector * How to select how to communicate. @@ -60,10 +60,10 @@ class GetTagsProcess extends TxrxProcess { Collection getTags(SCPConnection connection) throws IOException, ProcessException, InterruptedException { var tags = new TreeMap(); - for (var tag : range(0, getTagCount(connection)).toArray()) { - sendGet(new IPTagGet(connection.getChip(), tag), info -> { - if (info.isInUse()) { - tags.put(tag, info.getTag()); + for (var tagId : range(0, getTagCount(connection)).toArray()) { + sendGet(new IPTagGet(connection.getChip(), tagId), info -> { + if (info.inUse()) { + tags.put(tagId, info.tag()); } }); } @@ -74,7 +74,7 @@ Collection getTags(SCPConnection connection) private int getTagCount(SCPConnection connection) throws IOException, ProcessException, InterruptedException { var tagInfo = retrieve(new IPTagGetInfo(connection.getChip())); - return tagInfo.poolSize + tagInfo.fixedSize; + return tagInfo.poolSize() + tagInfo.fixedSize(); } /** @@ -94,10 +94,10 @@ private int getTagCount(SCPConnection connection) Map getTagUsage(SCPConnection connection) throws IOException, ProcessException, InterruptedException { var tagUsages = new TreeMap(); - for (var tag : range(0, getTagCount(connection)).toArray()) { - sendGet(new IPTagGet(connection.getChip(), tag), info -> { - if (info.isInUse()) { - tagUsages.put(info.getTag(), info.count); + for (var tagId : range(0, getTagCount(connection)).toArray()) { + sendGet(new IPTagGet(connection.getChip(), tagId), info -> { + if (info.inUse()) { + tagUsages.put(info.tag(), info.count()); } }); } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/MulticastRoutesControlProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/MulticastRoutesControlProcess.java index 38aad54997..bc5fddf491 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/MulticastRoutesControlProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/MulticastRoutesControlProcess.java @@ -17,11 +17,10 @@ import static java.lang.Byte.toUnsignedInt; import static java.lang.Integer.toUnsignedLong; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.machine.MachineDefaults.ROUTER_AVAILABLE_ENTRIES; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.transceiver.CommonMemoryLocations.ROUTING_TABLE_DATA; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.io.IOException; import java.nio.ByteBuffer; @@ -89,7 +88,7 @@ class MulticastRoutesControlProcess extends WriteMemoryProcess { * @return A buffer big enough to hold everything. */ private static ByteBuffer allocateBuffer(int entries) { - return allocate(BYTES_PER_ENTRY * (entries + 1)).order(LITTLE_ENDIAN); + return alloc(BYTES_PER_ENTRY * (entries + 1)); } private static void writeEntryToBuffer(ByteBuffer buffer, short index, diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ProcessException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ProcessException.java index 8bc70d6d12..3061c8d54e 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ProcessException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ProcessException.java @@ -18,6 +18,8 @@ import static java.lang.String.format; import static java.util.Objects.requireNonNull; +import java.io.Serial; + import uk.ac.manchester.spinnaker.messages.model.UnexpectedResponseCodeException; import uk.ac.manchester.spinnaker.messages.scp.SCPResult; import uk.ac.manchester.spinnaker.messages.sdp.SDPLocation; @@ -26,6 +28,7 @@ * Encapsulates exceptions from processes which communicate with some core/chip. */ public class ProcessException extends SpinnmanException { + @Serial private static final long serialVersionUID = 5198868033333540659L; private static final String S = " "; // five spaces @@ -70,8 +73,7 @@ private ProcessException(SDPLocation source, */ static ProcessException makeInstance(SDPLocation source, Throwable cause) throws InterruptedException { - if (requireNonNull(cause) instanceof UnexpectedResponseCodeException) { - var urc = (UnexpectedResponseCodeException) cause; + if (cause instanceof UnexpectedResponseCodeException urc) { if (urc.response == null) { return new ProcessException(source, cause, null); } @@ -110,10 +112,10 @@ static ProcessException makeInstance(SDPLocation source, // Fall through } } - if (cause instanceof InterruptedException) { - throw (InterruptedException) cause; + if (cause instanceof InterruptedException interrupt) { + throw interrupt; } - return new ProcessException(source, cause, null); + return new ProcessException(source, requireNonNull(cause), null); } /** @@ -165,6 +167,7 @@ private PermanentProcessException(SDPLocation source, * message, indicating that the packet length was wrong. */ public static final class BadPacketLength extends CallerProcessException { + @Serial private static final long serialVersionUID = 4329836896716525422L; private BadPacketLength(SDPLocation source, @@ -178,6 +181,7 @@ private BadPacketLength(SDPLocation source, * message, indicating that the checksum was wrong. */ public static final class BadChecksum extends CallerProcessException { + @Serial private static final long serialVersionUID = -5660270018252119601L; private BadChecksum(SDPLocation source, @@ -192,6 +196,7 @@ private BadChecksum(SDPLocation source, * destination. */ public static final class BadCommand extends CallerProcessException { + @Serial private static final long serialVersionUID = 2446636059917726286L; private BadCommand(SDPLocation source, @@ -205,6 +210,7 @@ private BadCommand(SDPLocation source, * message, indicating that the arguments to the command are wrong. */ public static final class InvalidArguments extends CallerProcessException { + @Serial private static final long serialVersionUID = 3907517289211998444L; private InvalidArguments(SDPLocation source, @@ -218,6 +224,7 @@ private InvalidArguments(SDPLocation source, * message, indicating that the SCP port was out of range. */ public static final class BadSCPPort extends CallerProcessException { + @Serial private static final long serialVersionUID = -5171910962257032626L; private BadSCPPort(SDPLocation source, @@ -232,6 +239,7 @@ private BadSCPPort(SDPLocation source, * timed out. */ public static final class TimedOut extends TransientProcessException { + @Serial private static final long serialVersionUID = -298985937364034661L; private TimedOut(SDPLocation source, @@ -246,6 +254,7 @@ private TimedOut(SDPLocation source, * for some reason. */ public static final class NoP2PRoute extends PermanentProcessException { + @Serial private static final long serialVersionUID = -6132417061161625508L; private NoP2PRoute(SDPLocation source, @@ -259,6 +268,7 @@ private NoP2PRoute(SDPLocation source, * message, indicating that the destination core number was out of range. */ public static final class BadCPUNumber extends CallerProcessException { + @Serial private static final long serialVersionUID = 6532417803149087690L; private BadCPUNumber(SDPLocation source, @@ -274,6 +284,7 @@ private BadCPUNumber(SDPLocation source, */ public static final class DeadDestination extends PermanentProcessException { + @Serial private static final long serialVersionUID = -3842030808096451015L; private DeadDestination(SDPLocation source, @@ -288,6 +299,7 @@ private DeadDestination(SDPLocation source, */ public static final class NoBufferAvailable extends TransientProcessException { + @Serial private static final long serialVersionUID = 3647501054775981197L; private NoBufferAvailable(SDPLocation source, @@ -302,6 +314,7 @@ private NoBufferAvailable(SDPLocation source, * messaging failed because the channel open failed. */ public static final class P2PNoReply extends TransientProcessException { + @Serial private static final long serialVersionUID = 2196366740196153289L; private P2PNoReply(SDPLocation source, @@ -316,6 +329,7 @@ private P2PNoReply(SDPLocation source, * the inter-SCAMP messaging rejected the message. */ public static final class P2PReject extends TransientProcessException { + @Serial private static final long serialVersionUID = -2903670314989693747L; private P2PReject(SDPLocation source, @@ -330,6 +344,7 @@ private P2PReject(SDPLocation source, * the inter-SCAMP messaging was busy. */ public static final class P2PBusy extends TransientProcessException { + @Serial private static final long serialVersionUID = 4445680981367158468L; private P2PBusy(SDPLocation source, @@ -344,6 +359,7 @@ private P2PBusy(SDPLocation source, * the inter-SCAMP messaging did not respond. */ public static final class P2PTimedOut extends TransientProcessException { + @Serial private static final long serialVersionUID = -7686611958418374003L; private P2PTimedOut(SDPLocation source, @@ -358,6 +374,7 @@ private P2PTimedOut(SDPLocation source, */ public static final class PacketTransmissionFailed extends TransientProcessException { + @Serial private static final long serialVersionUID = 5119831821960433468L; private PacketTransmissionFailed(SDPLocation source, diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ReadMemoryProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ReadMemoryProcess.java index 53373fdf44..06dcc9dfdc 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ReadMemoryProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/ReadMemoryProcess.java @@ -36,7 +36,7 @@ import uk.ac.manchester.spinnaker.transceiver.Accumulator.FileAccumulator; /** A process for reading memory on a SpiNNaker chip. */ -class ReadMemoryProcess extends TxrxProcess { +final class ReadMemoryProcess extends TxrxProcess { /** * @param connectionSelector * How to select how to communicate. diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/RouterControlProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/RouterControlProcess.java index dc711b1135..62b80e6774 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/RouterControlProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/RouterControlProcess.java @@ -47,7 +47,7 @@ * Access to the control facilities for a set of routers. Depends on access to * the extra monitor cores running on those chips. */ -class RouterControlProcess extends TxrxProcess { +final class RouterControlProcess extends TxrxProcess { private static final int REGISTER = 4; private static final int NUM_REGISTERS = 16; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/RuntimeControlProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/RuntimeControlProcess.java index e148b6e0f3..4c0ee5e262 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/RuntimeControlProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/RuntimeControlProcess.java @@ -16,12 +16,11 @@ package uk.ac.manchester.spinnaker.transceiver; import static java.lang.Math.min; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.util.Objects.requireNonNull; import static uk.ac.manchester.spinnaker.messages.Constants.CPU_IOBUF_ADDRESS_OFFSET; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.transceiver.Utils.getVcpuAddress; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.io.IOException; import java.nio.ByteBuffer; @@ -51,7 +50,7 @@ * * @author Donal Fellows */ -class RuntimeControlProcess extends TxrxProcess { +final class RuntimeControlProcess extends TxrxProcess { private static final int BUF_HEADER_BYTES = 16; private static final int BLOCK_HEADER_BYTES = 16; @@ -262,7 +261,7 @@ private void issueReadForIOBufHead(CoreLocation core, int blockID, private int saveIOBufHead(NextRead read, ByteBuffer bytes, int bytesToRead) { // Create a buffer for the data - var buffer = allocate(bytesToRead).order(LITTLE_ENDIAN); + var buffer = alloc(bytesToRead); // Put the data from this packet into the buffer int packetBytes = min(bytes.remaining(), bytesToRead); if (packetBytes > 0) { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/SpinnmanException.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/SpinnmanException.java index 0c02eade0f..320c63bfcb 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/SpinnmanException.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/SpinnmanException.java @@ -15,10 +15,13 @@ */ package uk.ac.manchester.spinnaker.transceiver; +import java.io.Serial; + /** * Basic exception from the transceiver. */ public class SpinnmanException extends Exception { + @Serial private static final long serialVersionUID = 4307580491294281556L; SpinnmanException(String message) { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Transceiver.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Transceiver.java index eee1d9b7c1..be74a056c0 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Transceiver.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Transceiver.java @@ -21,7 +21,6 @@ import static java.lang.Thread.sleep; import static java.net.InetAddress.getByAddress; import static java.nio.ByteBuffer.allocate; -import static java.util.Arrays.stream; import static java.util.Collections.unmodifiableSet; import static java.util.Objects.requireNonNull; import static org.apache.commons.io.IOUtils.buffer; @@ -37,7 +36,6 @@ import static uk.ac.manchester.spinnaker.messages.Constants.SCP_SCAMP_PORT; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_BOOT_CONNECTION_DEFAULT_PORT; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; -import static uk.ac.manchester.spinnaker.messages.bmp.SerialVector.SERIAL_LENGTH; import static uk.ac.manchester.spinnaker.messages.bmp.WriteFlashBuffer.FLASH_CHUNK_SIZE; import static uk.ac.manchester.spinnaker.messages.model.IPTagTimeOutWaitTime.TIMEOUT_2560_ms; import static uk.ac.manchester.spinnaker.messages.model.PowerCommand.POWER_OFF; @@ -76,15 +74,14 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadLocalRandom; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - import org.slf4j.Logger; import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.MustBeClosed; import com.google.errorprone.annotations.concurrent.GuardedBy; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.connections.BMPConnection; import uk.ac.manchester.spinnaker.connections.BootConnection; import uk.ac.manchester.spinnaker.connections.ConnectionSelector; @@ -114,7 +111,7 @@ import uk.ac.manchester.spinnaker.machine.tags.ReverseIPTag; import uk.ac.manchester.spinnaker.machine.tags.Tag; import uk.ac.manchester.spinnaker.messages.bmp.BMPRequest; -import uk.ac.manchester.spinnaker.messages.bmp.BMPSetLED; +import uk.ac.manchester.spinnaker.messages.bmp.SetBoardLEDs; import uk.ac.manchester.spinnaker.messages.bmp.GetBMPVersion; import uk.ac.manchester.spinnaker.messages.bmp.GetFPGAResetStatus; import uk.ac.manchester.spinnaker.messages.bmp.ReadADC; @@ -462,7 +459,7 @@ public Transceiver(InetAddress host, MachineVersion version, } connections.add(createScpConnection(desc.chip, desc.hostname)); } - for (Connection conn : connections) { + for (var conn : connections) { identifyConnection(conn); } scpSelector = makeConnectionSelector(); @@ -603,7 +600,7 @@ public Transceiver(MachineVersion version, allConnections.addAll(connections); // if there has been SCAMP connections given, build them if (scampConnections != null) { - for (ConnectionDescriptor desc : scampConnections) { + for (var desc : scampConnections) { if (desc.portNumber != null && desc.portNumber != SCP_SCAMP_PORT) { log.warn("ignoring unexpected SCAMP port: {}", @@ -612,7 +609,7 @@ public Transceiver(MachineVersion version, connections.add(createScpConnection(desc.chip, desc.hostname)); } } - for (Connection conn : connections) { + for (var conn : connections) { identifyConnection(conn); } scpSelector = makeConnectionSelector(); @@ -639,33 +636,31 @@ private ConnectionSelector makeConnectionSelector() { */ private void identifyConnection(Connection conn) { // locate the only boot send conn - if (conn instanceof BootConnection) { + if (conn instanceof BootConnection bc) { if (bootConnection != null) { throw new IllegalArgumentException( "Only a single BootSender can be specified"); } - bootConnection = (BootConnection) conn; + bootConnection = bc; } // Locate any connections listening on a UDP port - if (conn instanceof UDPConnection) { - registerConnection((UDPConnection) conn); + if (conn instanceof UDPConnection udpc) { + registerConnection(udpc); } // Locate any connections that can send SDP - if (conn instanceof SDPConnection) { - sdpConnections.add((SDPConnection) conn); + if (conn instanceof SDPConnection sdpc) { + sdpConnections.add(sdpc); } // Locate any connections that can send and receive SCP // If it is a BMP connection, add it here - if (conn instanceof BMPConnection) { - var bmpc = (BMPConnection) conn; + if (conn instanceof BMPConnection bmpc) { bmpConnections.add(bmpc); bmpSelectors.put(bmpc.getCoords(), new SingletonConnectionSelector<>(bmpc)); - } else if (conn instanceof SCPConnection) { - var scpc = (SCPConnection) conn; + } else if (conn instanceof SCPConnection scpc) { scpConnections.add(scpc); udpScpConnections.put(scpc.getRemoteIPAddress(), scpc); } @@ -713,32 +708,27 @@ private Object getSystemVariable(HasChipLocation chip, throws IOException, ProcessException, InterruptedException { var buffer = readMemory(chip, SYS_VARS.add(dataItem.offset), dataItem.type.value); - switch (dataItem.type) { - case BYTE: - return Byte.toUnsignedInt(buffer.get()); - case SHORT: - return Short.toUnsignedInt(buffer.getShort()); - case INT: - return buffer.getInt(); - case LONG: - return buffer.getLong(); - case BYTE_ARRAY: + return switch (dataItem.type) { + case BYTE -> Byte.toUnsignedInt(buffer.get()); + case SHORT -> Short.toUnsignedInt(buffer.getShort()); + case INT -> buffer.getInt(); + case LONG -> buffer.getLong(); + case BYTE_ARRAY -> { byte[] dst = (byte[]) dataItem.getDefault(); buffer.get(dst); - return dst; - case ADDRESS: - return new MemoryLocation(buffer.getInt()); - default: - // Unreachable - throw new IllegalStateException(); + yield dst; } + case ADDRESS -> new MemoryLocation(buffer.getInt()); + // Unreachable + default -> throw new IllegalStateException(); + }; } private ConnectionSelector bmpConnection(BMPCoords bmp) { if (!bmpSelectors.containsKey(bmp)) { throw new IllegalArgumentException( - "Unknown combination of cabinet (" + bmp.getCabinet() - + ") and frame (" + bmp.getFrame() + ")"); + "Unknown combination of cabinet (" + bmp.cabinet() + + ") and frame (" + bmp.frame() + ")"); } return bmpSelectors.get(bmp); } @@ -790,7 +780,7 @@ private void checkBMPConnections() try { var versionInfo = readBMPVersion(conn.getCoords(), conn.boards); if (!BMP_NAME.equals(versionInfo.name) || !BMP_MAJOR_VERSIONS - .contains(versionInfo.versionNumber.majorVersion)) { + .contains(versionInfo.versionNumber.majorVersion())) { throw new IOException(format( "The BMP at %s is running %s %s which is " + "incompatible with this transceiver, " @@ -902,8 +892,8 @@ void updateMachine() machine = machine.rebuild(); // update the SCAMP selector with the machine - if (scpSelector instanceof MachineAware) { - ((MachineAware) scpSelector).setMachine(machine); + if (scpSelector instanceof MachineAware ma) { + ma.setMachine(machine); } /* @@ -1108,24 +1098,7 @@ public void bootBoard(Map extraBootValues) * @return true exactly when they are compatible */ public static boolean isScampVersionCompatible(Version version) { - // The major version must match exactly - if (version.majorVersion != SCAMP_VERSION.majorVersion) { - return false; - } - - /* - * If the minor version matches, the patch version must be >= the - * required version - */ - if (version.minorVersion == SCAMP_VERSION.minorVersion) { - return version.revision >= SCAMP_VERSION.revision; - } - - /* - * If the minor version is > than the required version, the patch - * version is irrelevant - */ - return version.minorVersion > SCAMP_VERSION.minorVersion; + return version.compatibleWith(SCAMP_VERSION); } /** @@ -1164,9 +1137,9 @@ private TxrxProcess simpleProcess( private TxrxProcess simpleProcess(SDPConnection connector) throws IOException { // Avoid delegation of the connection if not needed - if (connector instanceof SCPConnection) { - return new TxrxProcess(new SingletonConnectionSelector<>( - (SCPConnection) connector), this); + if (connector instanceof SCPConnection scpConn) { + return new TxrxProcess(new SingletonConnectionSelector<>(scpConn), + this); } return new TxrxProcess(new SingletonConnectionSelector<>( new DelegatingSCPConnection(connector)), this); @@ -1803,7 +1776,7 @@ public void power(PowerCommand powerCommand, BMPCoords bmp, public void setLED(Collection leds, LEDAction action, BMPCoords bmp, Collection board) throws IOException, ProcessException, InterruptedException { - call(bmp, new BMPSetLED(leds, action, board)); + call(bmp, new SetBoardLEDs(leds, action, board)); } @Override @@ -1871,17 +1844,15 @@ public void writeBMPMemory(BMPCoords bmp, BMPBoard board, @Override public MemoryLocation getSerialFlashBuffer(BMPCoords bmp, BMPBoard board) throws IOException, ProcessException, InterruptedException { - return get(bmp, new ReadSerialVector(board)).getFlashBuffer(); + return get(bmp, new ReadSerialVector(board)).flashBuffer(); } @Override public String readBoardSerialNumber(BMPCoords bmp, BMPBoard board) throws IOException, ProcessException, InterruptedException { - var serialNumber = new int[SERIAL_LENGTH]; - get(bmp, new ReadSerialVector(board)).getSerialNumber() - .get(serialNumber); - return format("%08x-%08x-%08x-%08x", - stream(serialNumber).mapToObj(Integer::valueOf).toArray()); + var sn = get(bmp, new ReadSerialVector(board)).serialNumber(); + return format("%08x-%08x-%08x-%08x", // + sn.get(), sn.get(), sn.get(), sn.get()); } @Override @@ -1950,7 +1921,7 @@ public void writeFlash(@Valid BMPCoords bmp, @Valid BMPBoard board, } var serialVector = get(bmp, new ReadSerialVector(board)); - var workingBuffer = serialVector.getFlashBuffer(); + var workingBuffer = serialVector.flashBuffer(); var targetAddr = baseAddress; for (var buf : sliceUp(data, FLASH_CHUNK_SIZE)) { writeBMPMemory(bmp, board, workingBuffer, buf); @@ -2688,17 +2659,18 @@ ConnectionSelector> getBMPConnection() { return bmpSelectors; } - /** A simple description of a connnection to create. */ - public static final class ConnectionDescriptor { - /** What host to talk to. */ - private InetAddress hostname; - - /** What port to talk to, or {@code null} for default. */ - private Integer portNumber; - - /** What chip to talk to. */ - private ChipLocation chip; - + /** + * A simple description of a connection to create. + * + * @param hostname + * What host to talk to. + * @param portNumber + * What port to talk to, or {@code null} for default. + * @param chip + * What chip to talk to. + */ + public record ConnectionDescriptor(InetAddress hostname, Integer portNumber, + ChipLocation chip) { /** * Create a connection descriptor. * @@ -2709,9 +2681,7 @@ public static final class ConnectionDescriptor { */ public ConnectionDescriptor(InetAddress hostname, HasChipLocation chip) { - this.hostname = requireNonNull(hostname); - this.chip = chip.asChipLocation(); - this.portNumber = null; + this(requireNonNull(hostname), null, chip.asChipLocation()); } /** @@ -2726,9 +2696,7 @@ public ConnectionDescriptor(InetAddress hostname, */ public ConnectionDescriptor(InetAddress host, int port, HasChipLocation chip) { - this.hostname = requireNonNull(host); - this.chip = chip.asChipLocation(); - this.portNumber = port; + this(requireNonNull(host), (Integer) port, chip.asChipLocation()); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/TransceiverInterface.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/TransceiverInterface.java index 1aceb5d99d..7ebffd4863 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/TransceiverInterface.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/TransceiverInterface.java @@ -26,7 +26,6 @@ import static uk.ac.manchester.spinnaker.messages.Constants.NO_ROUTER_DIAGNOSTIC_FILTERS; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; -import static uk.ac.manchester.spinnaker.messages.Utils.wordAsBuffer; import static uk.ac.manchester.spinnaker.messages.model.AppID.DEFAULT; import static uk.ac.manchester.spinnaker.messages.model.CPUState.READY; import static uk.ac.manchester.spinnaker.messages.model.CPUState.RUN_TIME_EXCEPTION; @@ -37,6 +36,7 @@ import static uk.ac.manchester.spinnaker.transceiver.CommonMemoryLocations.SYS_VARS; import static uk.ac.manchester.spinnaker.transceiver.FillDataType.WORD; import static uk.ac.manchester.spinnaker.transceiver.Utils.getVcpuAddress; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.wordAsBuffer; import java.io.File; import java.io.IOException; @@ -51,15 +51,14 @@ import java.util.Set; import java.util.TreeMap; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.MustBeClosed; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.connections.ConnectionSelector; import uk.ac.manchester.spinnaker.connections.SCPConnection; import uk.ac.manchester.spinnaker.connections.SDPConnection; @@ -1905,7 +1904,7 @@ default void writeUser0(@Valid HasCoreLocation core, @NotNull MemoryLocation pointer) throws ProcessException, IOException, InterruptedException { writeMemory(core.getScampCore(), getUser0RegisterAddress(core), - pointer.address); + pointer.address()); } /** @@ -1946,7 +1945,7 @@ default void writeUser1(@Valid HasCoreLocation core, @NotNull MemoryLocation pointer) throws ProcessException, IOException, InterruptedException { writeMemory(core.getScampCore(), getUser1RegisterAddress(core), - pointer.address); + pointer.address()); } /** @@ -1987,7 +1986,7 @@ default void writeUser2(@Valid HasCoreLocation core, @NotNull MemoryLocation pointer) throws ProcessException, IOException, InterruptedException { writeMemory(core.getScampCore(), getUser2RegisterAddress(core), - pointer.address); + pointer.address()); } /** @@ -2547,10 +2546,10 @@ ByteBuffer readMemory(@Valid HasCoreLocation core, default ByteBuffer readMemory(@Valid HasCoreLocation core, @NotNull HeapElement element) throws IOException, ProcessException, InterruptedException { - if (element.isFree) { + if (element.isFree()) { return null; } - return readMemory(core, element.getDataAddress(), element.size); + return readMemory(core, element.dataAddress(), element.size()); } /** @@ -4015,8 +4014,8 @@ void setReinjectionEmergencyTimeout(@Valid HasCoreLocation monitorCore, default void setReinjectionEmergencyTimeout( @Valid HasCoreLocation monitorCore, @NotNull RouterTimeout timeout) throws IOException, ProcessException, InterruptedException { - setReinjectionEmergencyTimeout(monitorCore, timeout.mantissa, - timeout.exponent); + setReinjectionEmergencyTimeout(monitorCore, timeout.mantissa(), + timeout.exponent()); } /** @@ -4058,8 +4057,8 @@ void setReinjectionEmergencyTimeout(@Valid CoreSubsets monitorCores, default void setReinjectionEmergencyTimeout(@Valid CoreSubsets monitorCores, @NotNull RouterTimeout timeout) throws IOException, ProcessException, InterruptedException { - setReinjectionEmergencyTimeout(monitorCores, timeout.mantissa, - timeout.exponent); + setReinjectionEmergencyTimeout(monitorCores, timeout.mantissa(), + timeout.exponent()); } /** @@ -4081,7 +4080,7 @@ default void setReinjectionEmergencyTimeout(@Valid CoreSubsets monitorCores, @NotNull ReinjectionStatus status) throws IOException, ProcessException, InterruptedException { setReinjectionEmergencyTimeout(monitorCores, - status.getEmergencyTimeout()); + status.emergencyTimeout()); } /** @@ -4123,7 +4122,8 @@ void setReinjectionTimeout(@Valid HasCoreLocation monitorCore, default void setReinjectionTimeout(@Valid HasCoreLocation monitorCore, @NotNull RouterTimeout timeout) throws IOException, ProcessException, InterruptedException { - setReinjectionTimeout(monitorCore, timeout.mantissa, timeout.exponent); + setReinjectionTimeout(monitorCore, timeout.mantissa(), + timeout.exponent()); } /** @@ -4165,7 +4165,8 @@ void setReinjectionTimeout(@Valid CoreSubsets monitorCores, default void setReinjectionTimeout(@Valid CoreSubsets monitorCores, @NotNull RouterTimeout timeout) throws IOException, ProcessException, InterruptedException { - setReinjectionTimeout(monitorCores, timeout.mantissa, timeout.exponent); + setReinjectionTimeout(monitorCores, timeout.mantissa(), + timeout.exponent()); } /** @@ -4186,7 +4187,7 @@ default void setReinjectionTimeout(@Valid CoreSubsets monitorCores, default void setReinjectionTimeout(@Valid CoreSubsets monitorCores, @NotNull ReinjectionStatus status) throws IOException, ProcessException, InterruptedException { - setReinjectionTimeout(monitorCores, status.getTimeout()); + setReinjectionTimeout(monitorCores, status.timeout()); } /** diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/TxrxProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/TxrxProcess.java index e36e3b5216..cb2a2ead59 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/TxrxProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/TxrxProcess.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.InterruptedIOException; +import java.io.Serial; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -467,16 +468,7 @@ protected final void sendOneWayRequest(SCPRequest request) * States that a particular request failed with a particular exception. The * request should not be retried once this has been generated. */ - // TODO make into a record once on a new enough language profile - private static class Failure { - private final SCPRequest req; - - private final Exception exn; - - Failure(SCPRequest req, Exception exn) { - this.req = req; - this.exn = exn; - } + private record Failure(SCPRequest req, Exception exn) { } /** @@ -567,12 +559,9 @@ public void send() throws IOException { log.debug("Sending request {} with connection {}", request, connection); switch (request.sdpHeader.getFlags()) { - case REPLY_EXPECTED: - case REPLY_EXPECTED_NO_P2P: + case REPLY_EXPECTED, REPLY_EXPECTED_NO_P2P -> connection.send(requestData, seq); - break; - default: - connection.send(requestData); + default -> connection.send(requestData); } nextSendTime = nanoTime() + INTER_SEND_INTERVAL_NS; } @@ -731,8 +720,7 @@ void send(SCPRequest request, */ private Request registerRequest( SCPRequest request, Consumer callback) { - if (request instanceof ConnectionAwareMessage) { - ConnectionAwareMessage cam = (ConnectionAwareMessage) request; + if (request instanceof ConnectionAwareMessage cam) { cam.setConnection(connection); } synchronized (outstandingRequests) { @@ -940,6 +928,7 @@ public String toString() { * Indicates that a request timed out. */ static class SendTimedOutException extends SocketTimeoutException { + @Serial private static final long serialVersionUID = -7911020002602751941L; /** @@ -959,6 +948,7 @@ static class SendTimedOutException extends SocketTimeoutException { * Indicates that a request could not be sent. */ static class SendFailedException extends IOException { + @Serial private static final long serialVersionUID = -5555562816486761027L; /** @@ -982,6 +972,7 @@ static class SendFailedException extends IOException { */ static class DuplicateSequenceNumberException extends IllegalThreadStateException { + @Serial private static final long serialVersionUID = -4033792283948201730L; DuplicateSequenceNumberException() { diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/UnimplementedBMPTransceiver.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/UnimplementedBMPTransceiver.java index d811697484..9febb63fa2 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/UnimplementedBMPTransceiver.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/UnimplementedBMPTransceiver.java @@ -21,9 +21,8 @@ import java.nio.ByteBuffer; import java.util.Collection; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.MemoryLocation; import uk.ac.manchester.spinnaker.machine.board.BMPBoard; import uk.ac.manchester.spinnaker.machine.board.BMPCoords; diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Utils.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Utils.java index 2e48d79bd6..672fb87e49 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Utils.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Utils.java @@ -15,11 +15,9 @@ */ package uk.ac.manchester.spinnaker.transceiver; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.messages.Constants.CPU_INFO_BYTES; import static uk.ac.manchester.spinnaker.transceiver.CommonMemoryLocations.CPU_INFO; -import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.slice; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.io.IOException; import java.net.InetAddress; @@ -118,7 +116,7 @@ public static void sendPortTriggerMessage(UDPConnection connection, */ public static ByteBuffer newMessageBuffer() { // TODO How big should this buffer be? 256 or (256 + header size)? - return allocate(SPINNAKER_MESSAGE_BUFFER_SIZE).order(LITTLE_ENDIAN); + return alloc(SPINNAKER_MESSAGE_BUFFER_SIZE); } /** @@ -159,7 +157,7 @@ static void fill(ByteBuffer buffer, int start, int len, byte value) { */ static int crc(ByteBuffer buffer, int start, int len) { var crc = new CRC32(); - crc.update(slice(buffer, start, len)); + crc.update(buffer.slice(start, len)); return (int) crc.getValue(); } } diff --git a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/WriteMemoryFloodProcess.java b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/WriteMemoryFloodProcess.java index d35388ab0b..d8b0b4e2e8 100644 --- a/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/WriteMemoryFloodProcess.java +++ b/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/WriteMemoryFloodProcess.java @@ -15,12 +15,12 @@ */ package uk.ac.manchester.spinnaker.transceiver; -import static java.lang.Math.ceil; import static java.nio.ByteBuffer.allocate; import static org.apache.commons.io.IOUtils.buffer; import static uk.ac.manchester.spinnaker.messages.Constants.UDP_MESSAGE_MAX_SIZE; import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.read; import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.sliceUp; +import static uk.ac.manchester.spinnaker.utils.MathUtils.ceildiv; import java.io.File; import java.io.FileInputStream; @@ -36,7 +36,7 @@ import uk.ac.manchester.spinnaker.messages.scp.FloodFillStart; /** A process for writing memory on multiple SpiNNaker chips at once. */ -class WriteMemoryFloodProcess extends TxrxProcess { +final class WriteMemoryFloodProcess extends TxrxProcess { /** * @param connectionSelector * How to select how to communicate. @@ -51,10 +51,8 @@ class WriteMemoryFloodProcess extends TxrxProcess { super(connectionSelector, retryTracker); } - private static final float BPW = 4.0F; - private static int numBlocks(int numBytes) { - return (int) ceil(ceil(numBytes / BPW) / UDP_MESSAGE_MAX_SIZE); + return ceildiv(numBytes, UDP_MESSAGE_MAX_SIZE); } /** diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/alloc/client/JsonTest.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/alloc/client/JsonTest.java index d1337ae8aa..37a9419840 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/alloc/client/JsonTest.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/alloc/client/JsonTest.java @@ -67,8 +67,16 @@ void jobDescription() throws Exception { @Test void jobs() throws Exception { // Deserialize only - var jobs = "{\"jobs\":[\"http:foo1\",\"http:foo2\"]," - + "\"prev\":\"http:foo3\",\"next\":\"http:foo4\"}"; + var jobs = """ + { + "jobs": [ + "http:foo1", + "http:foo2" + ], + "prev": "http:foo3", + "next": "http:foo4" + } + """; assertEquals(2, deserialize(jobs, Jobs.class).jobs.size()); } @@ -76,23 +84,56 @@ void jobs() throws Exception { void machines() throws Exception { // Deserialize only // also BriefMachineDescription, DeadLink and Direction - var ms = "{\"machines\":[{\"name\":\"foo\",\"dead-links\":[[" - + "{\"board\":{\"x\":0,\"y\":0,\"z\":0},\"direction\":0}," - + "{\"board\":{\"x\":0,\"y\":0,\"z\":0},\"direction\":1}]]}]}"; + var ms = """ + { + "machines": [ + { + "name": "foo", + "dead-links": [ + [ { + "board": { + "x": 0, + "y": 0, + "z": 0 + }, + "direction": 0 + }, { + "board": { + "x": 0, + "y": 0, + "z": 0 + }, + "direction": 1 + } ] + ] + } + ] + } + """; assertEquals(1, deserialize(ms, Machines.class).machines.size()); } @Test void power() throws Exception { - var power = "{\"power\":\"OFF\"}"; + var power = """ + { + "power": "OFF" + } + """; assertNotNull(deserialize(power, Power.class)); } @Test void rootInfo() throws Exception { - var rootInfo = "{\"csrf-header\":\"a\",\"csrf-token\":\"b\"," - + "\"jobs-uri\":\"http:c\",\"machines-uri\":\"http:d\"," - + "\"version\":\"1.2.3\"}"; + var rootInfo = """ + { + "csrf-header": "a", + "csrf-token": "b", + "jobs-uri": "http:c", + "machines-uri": "http:d", + "version": "1.2.3" + } + """; var ri = deserialize(rootInfo, RootInfo.class); assertNotNull(ri); assertEquals("a", ri.csrfHeader); @@ -104,11 +145,19 @@ void rootInfo() throws Exception { @Test void whereis() throws Exception { - var whereIs = "{\"job-id\":123,\"job-ref\":\"http:/0\"," - + "\"job-chip\":[0,0],\"chip\":[1,1]," - + "\"machine-name\":\"gorp\",\"machine-ref\":\"http:/1\"," - + "\"board-chip\":[2,2],\"logical-board-coordinates\":[0,1,2]," - + "\"physical-board-coordinates\":[3,4,5]}"; + var whereIs = """ + { + "job-id": 123, + "job-ref": "http:/0", + "job-chip": [0, 0], + "chip": [1, 1], + "machine-name": "gorp", + "machine-ref": "http:/1", + "board-chip": [2, 2], + "logical-board-coordinates": [0, 1, 2], + "physical-board-coordinates": [3, 4, 5] + } + """; var wi = deserialize(whereIs, WhereIs.class); assertNotNull(wi); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestCpuInfo.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestCpuInfo.java index f2b4d4440c..657f79f701 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestCpuInfo.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestCpuInfo.java @@ -15,20 +15,18 @@ */ package uk.ac.manchester.spinnaker.messages.model; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static org.junit.jupiter.api.Assertions.*; -import java.nio.ByteBuffer; - import org.junit.jupiter.api.Test; import uk.ac.manchester.spinnaker.machine.CoreLocation; +import uk.ac.manchester.spinnaker.utils.ByteBufferUtils; class TestCpuInfo { @Test void testCreateWithBlankBuffer() { - var b = ByteBuffer.allocate(256).order(LITTLE_ENDIAN); + var b = ByteBufferUtils.alloc(256); var c = new CPUInfo(new CoreLocation(0, 0, 0), b); assertEquals(0, c.getApplicationMailboxDataAddress()); } diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestVersion.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestVersion.java index 8f10959833..8505e441b1 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestVersion.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestVersion.java @@ -26,33 +26,33 @@ public class TestVersion { @Test public void testThreeUnquoted() { - var version = new Version("1.2.3"); - assertEquals(1, version.majorVersion); - assertEquals(2, version.minorVersion); - assertEquals(3, version.revision); + var version = Version.parse("1.2.3"); + assertEquals(1, version.majorVersion()); + assertEquals(2, version.minorVersion()); + assertEquals(3, version.revision()); } @Test public void testThreeQuoted() { - var version = new Version("\"1.2.3\""); - assertEquals(1, version.majorVersion); - assertEquals(2, version.minorVersion); - assertEquals(3, version.revision); + var version = Version.parse("\"1.2.3\""); + assertEquals(1, version.majorVersion()); + assertEquals(2, version.minorVersion()); + assertEquals(3, version.revision()); } @Test public void testTwoUnquoted() { - var version = new Version("1.2"); - assertEquals(1, version.majorVersion); - assertEquals(2, version.minorVersion); - assertEquals(0, version.revision); + var version = Version.parse("1.2"); + assertEquals(1, version.majorVersion()); + assertEquals(2, version.minorVersion()); + assertEquals(0, version.revision()); } @Test public void testOneQuoted() { - var version = new Version("\"1\""); - assertEquals(1, version.majorVersion); - assertEquals(0, version.minorVersion); - assertEquals(0, version.revision); + var version = Version.parse("\"1\""); + assertEquals(1, version.majorVersion()); + assertEquals(0, version.minorVersion()); + assertEquals(0, version.revision()); } } diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestVersionInfo.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestVersionInfo.java index 21a63a2bf1..6e042b6ced 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestVersionInfo.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/model/TestVersionInfo.java @@ -15,9 +15,8 @@ */ package uk.ac.manchester.spinnaker.messages.model; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static org.junit.jupiter.api.Assertions.*; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; @@ -29,7 +28,7 @@ class TestVersionInfo { private ByteBuffer packVersionData(int arg1, int arg2, int arg3, byte[] data) { - var buffer = allocate(25).order(LITTLE_ENDIAN); + var buffer = alloc(25); buffer.putInt(arg1).putInt(arg2).putInt(arg3).put(data).flip(); return buffer; } @@ -93,7 +92,7 @@ void testInvalidSizedVersionData() throws UnsupportedEncodingException { var data = "my/spinnaker".getBytes("ASCII"); // Oh arg3, where art thou? - var versionData = allocate(21).order(LITTLE_ENDIAN); + var versionData = alloc(21); versionData.putInt(arg1).putInt(arg2)/*.putInt(arg3)*/.put(data).flip(); assertThrows(IllegalArgumentException.class, () -> { diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/GeneralMessageTest.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/GeneralMessageTest.java index 4110181f89..9104dd98f9 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/GeneralMessageTest.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/GeneralMessageTest.java @@ -39,9 +39,9 @@ import uk.ac.manchester.spinnaker.machine.HasCoreLocation; import uk.ac.manchester.spinnaker.machine.board.BMPBoard; -import uk.ac.manchester.spinnaker.messages.bmp.BMPReadMemory; -import uk.ac.manchester.spinnaker.messages.bmp.BMPSetLED; -import uk.ac.manchester.spinnaker.messages.bmp.BMPWriteMemory; +import uk.ac.manchester.spinnaker.messages.bmp.ReadBMPMemory; +import uk.ac.manchester.spinnaker.messages.bmp.SetBoardLEDs; +import uk.ac.manchester.spinnaker.messages.bmp.WriteBMPMemory; import uk.ac.manchester.spinnaker.messages.bmp.EraseFlash; import uk.ac.manchester.spinnaker.messages.bmp.GetBMPVersion; import uk.ac.manchester.spinnaker.messages.bmp.GetFPGAResetStatus; @@ -370,19 +370,19 @@ void writeMemory() { class Bmp { @Test void readMemory() { - assertEquals(NO_PAYLOAD, length(new BMPReadMemory(BOARD, NULL, 4))); + assertEquals(NO_PAYLOAD, length(new ReadBMPMemory(BOARD, NULL, 4))); } @Test - void setLed() { + void setBoardLeds() { assertEquals(NO_PAYLOAD, - length(new BMPSetLED(asList(0), TOGGLE, asList(BOARD)))); + length(new SetBoardLEDs(asList(0), TOGGLE, asList(BOARD)))); } @Test void writeMemory() { var b = allocate(128); - assertEquals(152, length(new BMPWriteMemory(BOARD, NULL, b))); + assertEquals(152, length(new WriteBMPMemory(BOARD, NULL, b))); } @Test diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestCountState.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestCountState.java index b6f6dc88e9..35c6fcdae1 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestCountState.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestCountState.java @@ -15,9 +15,8 @@ */ package uk.ac.manchester.spinnaker.messages.scp; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static org.junit.jupiter.api.Assertions.*; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.io.UnsupportedEncodingException; @@ -54,7 +53,7 @@ void testNewStateResponse() throws UnexpectedResponseCodeException { int srcX = 0x7; int srcY = 0x0; - var data = allocate(18).order(LITTLE_ENDIAN).putShort(PADDING); + var data = alloc(18).putShort(PADDING); data.put(flags.value); data.put((byte) tag); data.put((byte) destPortCPU); @@ -95,7 +94,7 @@ void testFailedDeserialise() throws UnsupportedEncodingException { byte srcX = 0x7; byte srcY = 0x0; - var data = allocate(41).order(LITTLE_ENDIAN).putShort(PADDING); + var data = alloc(41).putShort(PADDING); data.put(flags.value).put(tag).put(destPortCPU).put(srcPortCPU); data.put(destY).put(destX).put(srcY).put(srcX); data.putShort(rc.value).putShort(seq).putShort(p2pAddr); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestOKResponse.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestOKResponse.java index c7e2c27020..f075ada573 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestOKResponse.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestOKResponse.java @@ -15,10 +15,7 @@ */ package uk.ac.manchester.spinnaker.messages.scp; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_DPRI; import static uk.ac.manchester.spinnaker.messages.scp.SCPResult.RC_OK; import static uk.ac.manchester.spinnaker.messages.scp.SCPResult.RC_TIMEOUT; @@ -27,6 +24,7 @@ import org.junit.jupiter.api.Test; import uk.ac.manchester.spinnaker.messages.model.UnexpectedResponseCodeException; +import uk.ac.manchester.spinnaker.utils.ByteBufferUtils; class TestOKResponse { private static final short PADDING = 0; @@ -60,7 +58,7 @@ void testReadOKResponse() throws UnexpectedResponseCodeException { short srcXYShort = (short) (srcX << 8 | srcY); short seq = 103; - var bytes = allocate(14).order(LITTLE_ENDIAN).putShort(PADDING); + var bytes = ByteBufferUtils.alloc(14).putShort(PADDING); bytes.putShort(flagTagShort).putShort(destSourceShort); bytes.putShort(destXYShort).putShort(srcXYShort); bytes.putShort(result).putShort(seq).flip(); @@ -93,7 +91,7 @@ void testNotOKResponse() { short srcXYShort = (short) (srcX << 8 | srcY); short seq = 103; - var bytes = allocate(14).order(LITTLE_ENDIAN).putShort(PADDING); + var bytes = ByteBufferUtils.alloc(14).putShort(PADDING); bytes.putShort(flagTagShort).putShort(destSourceShort); bytes.putShort(destXYShort).putShort(srcXYShort); bytes.putShort(result).putShort(seq).flip(); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestVersion.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestVersion.java index c466b4400d..d999613d4f 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestVersion.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/messages/scp/TestVersion.java @@ -16,13 +16,12 @@ package uk.ac.manchester.spinnaker.messages.scp; import static java.lang.String.join; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.nio.charset.StandardCharsets.US_ASCII; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; import static uk.ac.manchester.spinnaker.messages.scp.SCPCommand.CMD_VER; import static uk.ac.manchester.spinnaker.messages.scp.SCPResult.RC_OK; import static uk.ac.manchester.spinnaker.messages.sdp.SDPHeader.Flag.REPLY_NOT_EXPECTED; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import org.junit.jupiter.api.Test; @@ -66,7 +65,7 @@ void testParseVersionResponseFormat1() byte srcX = 0x7; byte srcY = 0x0; - var data = allocate(41).order(LITTLE_ENDIAN).putShort(PADDING); + var data = alloc(41).putShort(PADDING); data.put(flags).put(tag).put(destPortCPU).put(srcPortCPU); data.put(destY).put(destX).put(srcY).put(srcX); data.putShort(rc).putShort(seq).putShort(p2pAddr); @@ -107,7 +106,7 @@ void testParseVersionResponseFormat2() byte srcX = 0x7; byte srcY = 0x0; - var data = allocate(60).order(LITTLE_ENDIAN).putShort(PADDING); + var data = alloc(60).putShort(PADDING); data.put(flags).put(tag).put(destPortCpu).put(srcPortCpu); data.put(destY).put(destX).put(srcY).put(srcX); data.putShort(rc).putShort(seq).putShort(p2pAddr); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/SupportUtils.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/SupportUtils.java index 46f20a2ead..5bfe1f949b 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/SupportUtils.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/SupportUtils.java @@ -48,6 +48,13 @@ static final void assertTimeout(long before, long after) { static final int TIMEOUT = 101; interface IConnection extends Closeable { + /** + * Arrange for a message to be sent. + * + * @param jsonString + * The JSON message to be sent. Will be parsed and + * reformatted for the protocol. + */ default void send(String jsonString) { send(new JSONObject(jsonString)); } @@ -66,6 +73,7 @@ void advancedEmulationMode(BlockingDeque send, int getPort(); } + @FunctionalInterface interface WithConn { void act(IServer s, SpallocClient c, MockServer.Joinable bgAccept) throws Exception; @@ -82,6 +90,7 @@ static void withConnection(WithConn op) throws Exception { } } + @FunctionalInterface interface WithConnConn { void act(SpallocClient client, IConnection serviceSideConnection) throws Exception; @@ -101,6 +110,7 @@ static void withConnectedConnection(WithConnConn op) throws Exception { } } + @FunctionalInterface interface WithAdvancedConn { void act(SpallocClient c) throws Exception; } diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestClient.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestClient.java index 2c998e6f76..51d4caf46a 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestClient.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestClient.java @@ -38,7 +38,7 @@ import uk.ac.manchester.spinnaker.spalloc.exceptions.SpallocServerException; import uk.ac.manchester.spinnaker.spalloc.messages.BoardCoordinates; import uk.ac.manchester.spinnaker.spalloc.messages.BoardPhysicalCoordinates; -import uk.ac.manchester.spinnaker.spalloc.messages.Command; +import uk.ac.manchester.spinnaker.spalloc.messages.CustomIntCommand; import uk.ac.manchester.spinnaker.spalloc.messages.ExceptionResponse; import uk.ac.manchester.spinnaker.spalloc.messages.JobsChangedNotification; import uk.ac.manchester.spinnaker.spalloc.messages.MachinesChangedNotification; @@ -47,16 +47,28 @@ import uk.ac.manchester.spinnaker.spalloc.messages.WhereIs; class TestClient { - static class MockCommand extends Command { - MockCommand(String name, int arg, String key, Object val) { - super(name); - addArg(arg); - addKwArg(key, val); + static class MockCommand extends CustomIntCommand { + MockCommand(int arg, Object val) { + super("foo", arg); + addKwArg("bar", val); } } - private static final String MOCK_RECEIVED_MESSAGE = - "{\"command\": \"foo\", \"args\": [1], \"kwargs\": {\"bar\": 2}}"; + private static final String MOCK_RECEIVED_MESSAGE = """ + { + "command": "foo", + "args": [1], + "kwargs": { + "bar": 2 + } + } + """; + + private static final String VOID_RETURN = """ + { + "return": null + } + """; @Test void testConnectNoServer() throws Exception { @@ -119,7 +131,11 @@ void testReceiveJson() throws Exception { assertTimeout(before, after); // Transfer an actual message - sc.send("{\"return\": \"bar\"}"); + sc.send(""" + { + "return": "bar" + } + """); assertEquals("\"bar\"", ((ReturnResponse) c.receiveResponse(null)) .getReturnValue()); @@ -140,8 +156,16 @@ void testReceiveJson() throws Exception { } // Test message ordering - sc.send("{\"return\": \"foo\"}"); - sc.send("{\"return\": \"bar\"}"); + sc.send(""" + { + "return": "foo" + } + """); + sc.send(""" + { + "return": "bar" + } + """); assertEquals("\"foo\"", ((ReturnResponse) c.receiveResponse(null)) .getReturnValue()); @@ -150,15 +174,27 @@ void testReceiveJson() throws Exception { .getReturnValue()); // Test other message types - sc.send("{\"exception\": \"bar\"}"); + sc.send(""" + { + "exception": "bar" + } + """); assertEquals("bar", ((ExceptionResponse) c.receiveResponse(null)) .getException()); - sc.send("{\"machines_changed\": [\"foo\",\"bar\"]}"); + sc.send(""" + { + "machines_changed": ["foo", "bar"] + } + """); assertEquals(List.of("foo", "bar"), ((MachinesChangedNotification) c.receiveResponse(null)) .getMachinesChanged()); - sc.send("{\"jobs_changed\": [1, 2]}"); + sc.send(""" + { + "jobs_changed": [1, 2] + } + """); assertEquals(List.of(1, 2), ((JobsChangedNotification) c.receiveResponse(null)) .getJobsChanged()); @@ -180,9 +216,13 @@ void testSendJson() throws Exception { try (var sc = bgAccept.join()) { // Make sure we can send JSON c.sendCommand(new VersionCommand(), 250); - JSONAssert.assertEquals( - "{\"command\":\"version\",\"args\":[],\"kwargs\":{}}", - sc.recv(), true); + JSONAssert.assertEquals(""" + { + "command":"version", + "args":[], + "kwargs":{} + } + """, sc.recv(), true); } }); } @@ -191,20 +231,34 @@ void testSendJson() throws Exception { void testCall() throws Exception { withConnectedConnection((c, s) -> { // Basic calls should work - s.send("{\"return\": \"Woo\"}"); - assertEquals("\"Woo\"", - c.call(new MockCommand("foo", 1, "bar", 2), null)); + s.send(""" + { + "return": "Woo" + } + """); + assertEquals("\"Woo\"", c.call(new MockCommand(1, 2), null)); JSONAssert.assertEquals(MOCK_RECEIVED_MESSAGE, s.recv(), true); /* * Should be able to cope with notifications arriving before return * value */ - s.send("{\"jobs_changed\": [1]}"); - s.send("{\"jobs_changed\": [2]}"); - s.send("{\"return\": \"Woo\"}"); - assertEquals("\"Woo\"", - c.call(new MockCommand("foo", 1, "bar", 2), null)); + s.send(""" + { + "jobs_changed": [1] + } + """); + s.send(""" + { + "jobs_changed": [2] + } + """); + s.send(""" + { + "return": "Woo" + } + """); + assertEquals("\"Woo\"", c.call(new MockCommand(1, 2), null)); JSONAssert.assertEquals(MOCK_RECEIVED_MESSAGE, s.recv(), true); assertEquals(new JobsChangedNotification(1), c.waitForNotification(-1)); @@ -216,16 +270,20 @@ void testCall() throws Exception { // Should be able to timeout immediately long before = System.currentTimeMillis(); assertThrows(SpallocProtocolTimeoutException.class, - () -> c.call(new MockCommand("foo", 1, "bar", 2), TIMEOUT)); + () -> c.call(new MockCommand(1, 2), TIMEOUT)); long after = System.currentTimeMillis(); JSONAssert.assertEquals(MOCK_RECEIVED_MESSAGE, s.recv(), true); assertTimeout(before, after); // Should be able to timeout after getting a notification - s.send("{\"jobs_changed\": [3]}"); + s.send(""" + { + "jobs_changed": [3] + } + """); before = System.currentTimeMillis(); assertThrows(SpallocProtocolTimeoutException.class, - () -> c.call(new MockCommand("foo", 1, "bar", 2), TIMEOUT)); + () -> c.call(new MockCommand(1, 2), TIMEOUT)); after = System.currentTimeMillis(); JSONAssert.assertEquals(MOCK_RECEIVED_MESSAGE, s.recv(), true); assertTimeout(before, after); @@ -234,9 +292,13 @@ void testCall() throws Exception { assertNull(c.waitForNotification(-1)); // Exceptions should transfer - s.send("{\"exception\": \"something informative\"}"); + s.send(""" + { + "exception": "something informative" + } + """); var t = assertThrows(SpallocServerException.class, - () -> c.call(new MockCommand("foo", 1, "bar", 2), TIMEOUT)); + () -> c.call(new MockCommand(1, 2), TIMEOUT)); assertEquals("something informative", t.getMessage()); }); } @@ -255,11 +317,19 @@ void testWaitForNotification() throws Exception { // If notifications queued during call, should just return // those - s.send("{\"jobs_changed\": [1]}"); - s.send("{\"jobs_changed\": [2]}"); + s.send(""" + { + "jobs_changed": [1] + } + """); + s.send(""" + { + "jobs_changed": [2] + } + """); s.send("{\"return\": \"Woo\"}"); assertEquals("\"Woo\"", - c.call(new MockCommand("foo", 1, "bar", 2), null)); + c.call(new MockCommand(1, 2), null)); JSONAssert.assertEquals(MOCK_RECEIVED_MESSAGE, s.recv(), true); assertEquals(new JobsChangedNotification(1), c.waitForNotification()); @@ -267,7 +337,11 @@ void testWaitForNotification() throws Exception { c.waitForNotification()); // If no notifications queued, should listen for them - s.send("{\"jobs_changed\": [3]}"); + s.send(""" + { + "jobs_changed": [3] + } + """); assertEquals(new JobsChangedNotification(3), c.waitForNotification()); }); @@ -278,79 +352,151 @@ void testWaitForNotification() throws Exception { void testCommandCreateJob() throws Exception { withConnectedConnection((c, s) -> { // Old style create_job - s.send("{\"return\": 123}"); + s.send(""" + { + "return": 123 + } + """); Map kwargs = Map.of("bar", 2, "owner", "dummy"); assertEquals(123, c.createJob(List.of(1), kwargs)); - JSONAssert.assertEquals( - "{\"command\": \"create_job\", \"args\": [1], " - + "\"kwargs\": {\"owner\": \"dummy\"}}", + JSONAssert.assertEquals(""" + { + "command": "create_job", + "args": [1], + "kwargs": { + "owner": "dummy" + } + } + """, s.recv(), true); // New style create_job - s.send("{\"return\": 123}"); + s.send(""" + { + "return": 123 + } + """); assertEquals(123, c.createJob(new CreateJob(1).owner("dummy"))); - JSONAssert.assertEquals( - "{\"command\": \"create_job\", \"args\": [1], " - + "\"kwargs\": {\"owner\": \"dummy\"}}", - s.recv(), true); + JSONAssert.assertEquals(""" + { + "command": "create_job", + "args": [1], + "kwargs": { + "owner": "dummy" + } + } + """, s.recv(), true); }); } @Test void testCommandListJobs() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":[{\"job_id\":123},{\"job_id\":99}]}"); + s.send(""" + { + "return": [ + {"job_id": 123}, + {"job_id": 99} + ] + } + """); var result = c.listJobs(); assertEquals(2, result.size()); assertEquals(123, result.get(0).getJobID()); assertEquals(99, result.get(1).getJobID()); - JSONAssert.assertEquals("{\"command\": \"list_jobs\", " - + "\"args\": [], \"kwargs\": {}}", s.recv(), true); + JSONAssert.assertEquals(""" + { + "command": "list_jobs", + "args": [], + "kwargs": {} + } + """, s.recv(), true); }); } @Test void testCommandListMachines() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":[{\"name\":\"foo\"},{\"name\":\"bar\"}]}"); + s.send(""" + { + "return": [ + {"name": "foo"}, {"name": "bar"} + ] + } + """); var result = c.listMachines(); assertEquals(2, result.size()); assertEquals("foo", result.get(0).getName()); assertEquals("bar", result.get(1).getName()); - JSONAssert.assertEquals("{\"command\": \"list_machines\", " - + "\"args\": [], \"kwargs\": {}}", s.recv(), true); + JSONAssert.assertEquals(""" + { + "command": "list_machines", + "args": [], + "kwargs": {} + } + """, s.recv(), true); }); } @Test void testCommandDestroyJob() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":null}"); + s.send(VOID_RETURN); c.destroyJob(123, "gorp"); - JSONAssert.assertEquals( - "{\"command\": \"destroy_job\", " + "\"args\": [123], " - + "\"kwargs\": {\"reason\":\"gorp\"}}", - s.recv(), true); + JSONAssert.assertEquals(""" + { + "command": "destroy_job", + "args": [123], + "kwargs": { + "reason": "gorp" + } + } + """, s.recv(), true); }); } @Test void testCommandGetBoardPosition() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":[4,5,6]}"); + s.send(""" + { + "return": [4, 5, 6] + } + """); var pc = c.getBoardPosition("gorp", new BoardCoordinates(1, 2, 3)); assertEquals(new BoardPhysicalCoordinates(4, 5, 6), pc); - JSONAssert.assertEquals("{\"command\": \"get_board_position\", " - + "\"args\": [], \"kwargs\": " - + "{\"machine_name\":\"gorp\",\"x\":1,\"y\":2,\"z\":3}}", - s.recv(), true); + JSONAssert.assertEquals(""" + { + "command": "get_board_position", + "args": [], + "kwargs": { + "machine_name": "gorp", + "x": 1, + "y": 2, + "z": 3 + } + } + """, s.recv(), true); - s.send("{\"return\":[7,8,9]}"); + s.send(""" + { + "return": [7, 8, 9] + } + """); var lc = c.getBoardPosition("gorp", pc); assertEquals(new BoardCoordinates(7, 8, 9), lc); - JSONAssert.assertEquals("{\"command\": \"get_board_at_position\", " - + "\"args\": [], \"kwargs\": " - + "{\"machine_name\":\"gorp\",\"x\":4,\"y\":5,\"z\":6}}", + JSONAssert.assertEquals(""" + { + "command": "get_board_at_position", + "args": [], + "kwargs": { + "machine_name": "gorp", + "x": 4, + "y": 5, + "z": 6 + } + } + """, s.recv(), true); }); } @@ -358,102 +504,151 @@ void testCommandGetBoardPosition() throws Exception { @Test void testCommandGetJobMachineInfo() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":{\"boards\": [[1,2,3]]," - + "\"connections\": [[[1,2],\"gorp\"]]}}"); + s.send(""" + { + "return": { + "boards": [ + [1, 2, 3] + ], + "connections": [ + [[1, 2], "gorp"] + ] + } + } + """); var result = c.getJobMachineInfo(123); var boards = result.getBoards(); assertEquals(1, boards.size()); assertEquals(new BoardCoordinates(1, 2, 3), boards.get(0)); var conns = result.getConnections(); assertEquals(1, conns.size()); - assertEquals(new ChipLocation(1, 2), conns.get(0).getChip()); - assertEquals("gorp", conns.get(0).getHostname()); - JSONAssert.assertEquals( - "{\"command\": \"get_job_machine_info\", " - + "\"args\": [123], \"kwargs\": {}}", - s.recv(), true); + assertEquals(new ChipLocation(1, 2), conns.get(0).chip()); + assertEquals("gorp", conns.get(0).hostname()); + JSONAssert.assertEquals(""" + { + "command": "get_job_machine_info", + "args": [123], + "kwargs": {} + } + """, s.recv(), true); }); } @Test void testCommandGetJobState() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":{\"state\":3,\"power\":true}}"); + s.send(""" + { + "return": { + "state": 3, + "power": true + } + } + """); var result = c.getJobState(123); assertEquals(true, result.getPower()); assertEquals(READY, result.getState()); - JSONAssert.assertEquals( - "{\"command\": \"get_job_state\", " - + "\"args\": [123], \"kwargs\": {}}", - s.recv(), true); + JSONAssert.assertEquals(""" + { + "command": "get_job_state", + "args": [123], + "kwargs": {} + } + """, s.recv(), true); }); } @Test void testCommandNotifyJob() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":null}"); + s.send(VOID_RETURN); c.notifyJob(123, false); - JSONAssert.assertEquals( - "{\"command\": \"no_notify_job\", " - + "\"args\": [123], \"kwargs\": {}}", - s.recv(), true); - s.send("{\"return\":null}"); + JSONAssert.assertEquals(""" + { + "command": "no_notify_job", + "args": [123], + "kwargs": {} + } + """, s.recv(), true); + s.send(VOID_RETURN); c.notifyJob(123, true); - JSONAssert.assertEquals( - "{\"command\": \"notify_job\", " - + "\"args\": [123], \"kwargs\": {}}", - s.recv(), true); + JSONAssert.assertEquals(""" + { + "command": "notify_job", + "args": [123], + "kwargs": {} + } + """, s.recv(), true); }); } @Test void testCommandNotifyMachine() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":null}"); + s.send(VOID_RETURN); c.notifyMachine("foo", false); - JSONAssert.assertEquals( - "{\"command\": \"no_notify_machine\", " - + "\"args\": [\"foo\"], \"kwargs\": {}}", - s.recv(), true); - s.send("{\"return\":null}"); + JSONAssert.assertEquals(""" + { + "command": "no_notify_machine", + "args": ["foo"], + "kwargs": {} + } + """, s.recv(), true); + s.send(VOID_RETURN); c.notifyMachine("foo", true); - JSONAssert.assertEquals( - "{\"command\": \"notify_machine\", " - + "\"args\": [\"foo\"], \"kwargs\": {}}", - s.recv(), true); + JSONAssert.assertEquals(""" + { + "command": "notify_machine", + "args": ["foo"], + "kwargs": {} + } + """, s.recv(), true); }); } @Test void testCommandPower() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":null}"); + s.send(VOID_RETURN); c.powerOffJobBoards(123); - JSONAssert.assertEquals( - "{\"command\": \"power_off_job_boards\", " - + "\"args\": [123], \"kwargs\": {}}", - s.recv(), true); - s.send("{\"return\":null}"); + JSONAssert.assertEquals(""" + { + "command": "power_off_job_boards", + "args": [123], + "kwargs": {} + } + """, s.recv(), true); + s.send(VOID_RETURN); c.powerOnJobBoards(123); - JSONAssert.assertEquals( - "{\"command\": \"power_on_job_boards\", " - + "\"args\": [123], \"kwargs\": {}}", - s.recv(), true); + JSONAssert.assertEquals(""" + { + "command": "power_on_job_boards", + "args": [123], + "kwargs": {} + } + """, s.recv(), true); }); } @Test void testCommandVersion() throws Exception { withConnectedConnection((c, s) -> { - s.send("{\"return\":\"1.2.3\"}"); + s.send(""" + { + "return": "1.2.3" + } + """); var result = c.version(); - assertEquals(1, result.majorVersion); - assertEquals(2, result.minorVersion); - assertEquals(3, result.revision); - JSONAssert.assertEquals( - "{\"command\": \"version\", \"args\": [], \"kwargs\": {}}", - s.recv(), true); + assertEquals(1, result.majorVersion()); + assertEquals(2, result.minorVersion()); + assertEquals(3, result.revision()); + JSONAssert.assertEquals(""" + { + "command": "version", + "args": [], + "kwargs": {} + } + """, s.recv(), true); }); } @@ -462,42 +657,94 @@ void testCommandWhereIs() throws Exception { withConnectedConnection((c, s) -> { WhereIs result; - s.send("{\"return\":{\"machine\":\"gorp\",\"logical\":[2,3,4]}}"); + s.send(""" + { + "return": { + "machine": "gorp", + "logical": [2, 3, 4] + } + } + """); result = c.whereIs(123, new ChipLocation(1, 2)); - assertEquals("gorp", result.getMachine()); - assertEquals(new BoardCoordinates(2, 3, 4), result.getLogical()); - JSONAssert.assertEquals( - "{\"command\": \"where_is\", \"args\": [], \"kwargs\": {" - + "\"chip_x\":1,\"chip_y\":2,\"job_id\":123}}", - s.recv(), true); + assertEquals("gorp", result.machine()); + assertEquals(new BoardCoordinates(2, 3, 4), result.logical()); + JSONAssert.assertEquals(""" + { + "command": "where_is", + "args": [], + "kwargs": { + "chip_x": 1, + "chip_y": 2, + "job_id": 123 + } + } + """, s.recv(), true); - s.send("{\"return\":{\"physical\":[2,3,4]}}"); + s.send(""" + { + "return": { + "physical": [2, 3, 4] + } + } + """); result = c.whereIs("gorp", new BoardCoordinates(1, 2, 3)); assertEquals(new BoardPhysicalCoordinates(2, 3, 4), - result.getPhysical()); - JSONAssert.assertEquals( - "{\"command\": \"where_is\", \"args\": [], \"kwargs\": {" - + "\"x\":1,\"y\":2,\"z\":3,\"machine\":\"gorp\"" - + "}}", - s.recv(), true); + result.physical()); + JSONAssert.assertEquals(""" + { + "command": "where_is", + "args": [], + "kwargs": { + "x": 1, + "y": 2, + "z": 3, + "machine": "gorp" + } + } + """, s.recv(), true); - s.send("{\"return\":{\"logical\":[2,3,4]}}"); + s.send(""" + { + "return": { + "logical": [2, 3, 4] + } + } + """); result = c.whereIs("gorp", new BoardPhysicalCoordinates(1, 2, 3)); - assertEquals(new BoardCoordinates(2, 3, 4), result.getLogical()); - JSONAssert.assertEquals( - "{\"command\": \"where_is\", \"args\": [], \"kwargs\": {" - + "\"cabinet\":1,\"frame\":2,\"board\":3," - + "\"machine\":\"gorp\"}}", - s.recv(), true); + assertEquals(new BoardCoordinates(2, 3, 4), result.logical()); + JSONAssert.assertEquals(""" + { + "command": "where_is", + "args": [], + "kwargs": { + "cabinet": 1, + "frame": 2, + "board": 3, + "machine": "gorp" + } + } + """, s.recv(), true); - s.send("{\"return\":{\"logical\":[2,3,4]}}"); + s.send(""" + { + "return": { + "logical": [2, 3, 4] + } + } + """); result = c.whereIs("gorp", new ChipLocation(0, 1)); - assertEquals(new BoardCoordinates(2, 3, 4), result.getLogical()); - JSONAssert.assertEquals( - "{\"command\": \"where_is\", \"args\": [], \"kwargs\": {" - + "\"chip_x\":0,\"chip_y\":1," - + "\"machine\":\"gorp\"}}", - s.recv(), true); + assertEquals(new BoardCoordinates(2, 3, 4), result.logical()); + JSONAssert.assertEquals(""" + { + "command": "where_is", + "args": [], + "kwargs": { + "chip_x": 0, + "chip_y": 1, + "machine": "gorp" + } + } + """, s.recv(), true); }); } } diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestJob.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestJob.java index 472e219258..e0fcbc15dd 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestJob.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestJob.java @@ -58,18 +58,63 @@ static void resetConfiguration() { */ private static BlockingDeque mockServerMessagesToSend() { var send = new LinkedBlockingDeque(); - send.offer("{\"return\": 123}"); - send.offer("{\"return\": null}"); - send.offer("{\"return\": {\"state\": 2, \"power\": false}}"); - send.offer("{\"return\": null}"); - send.offer("{\"jobs_changed\": [123]}"); - send.offer("{\"return\": {\"state\": 3, \"power\": true}}"); - send.offer("{\"return\": {\"connections\":[[[0,0],\"10.11.223.33\"]]," - + "\"width\":8," - + "\"machine_name\":\"Spin24b-223\"," - + "\"boards\":[[4,5,6], [7,8,9]]," - + "\"height\":8}}"); - send.offer("{\"return\": null}"); + send.offer(""" + { + "return": 123 + } + """); + send.offer(""" + { + "return": null + } + """); + send.offer(""" + { + "return": { + "state": 2, + "power": false + } + } + """); + send.offer(""" + { + "return": null + } + """); + send.offer(""" + { + "jobs_changed": [123] + } + """); + send.offer(""" + { + "return": { + "state": 3, + "power": true + } + } + """); + send.offer(""" + { + "return": { + "connections": [ + [[0,0], "10.11.223.33"] + ], + "width": 8, + "machine_name": "Spin24b-223", + "boards": [ + [4,5,6], + [7,8,9] + ], + "height": 8 + } + } + """); + send.offer(""" + { + "return": null + } + """); send.offer(STOP); return send; } @@ -83,33 +128,61 @@ private static BlockingDeque mockServerMessagesToSend() { private static void assertMockServerReceived( BlockingDeque received) throws JSONException, InterruptedException { - JSONAssert.assertEquals("{\"command\": \"create_job\", " - + "\"args\": [1, 2, 3], \"kwargs\": {" - + "\"keepalive\": 1, \"owner\": \"java test harness\", " - + "\"tags\": [\"default\"]}}", received.take(), true); - JSONAssert.assertEquals( - "{\"command\": \"power_on_job_boards\", \"args\": [123], " - + "\"kwargs\": {}}", - received.take(), true); - JSONAssert.assertEquals( - "{\"command\": \"get_job_state\", \"args\": [123], " - + "\"kwargs\": {}}", - received.take(), true); - JSONAssert - .assertEquals("{\"command\": \"notify_job\", \"args\": [123], " - + "\"kwargs\": {}}", received.take(), true); - JSONAssert.assertEquals( - "{\"command\": \"get_job_state\", \"args\": [123], " - + "\"kwargs\": {}}", - received.take(), true); - JSONAssert.assertEquals( - "{\"command\": \"get_job_machine_info\", \"args\": [123], " - + "\"kwargs\": {}}", - received.take(), true); - JSONAssert.assertEquals( - "{\"command\": \"destroy_job\", \"args\": [123], " - + "\"kwargs\": {\"reason\": \"abc\"}}", - received.take(), true); + JSONAssert.assertEquals(""" + { + "command": "create_job", + "args": [1, 2, 3], + "kwargs": { + "keepalive": 1, + "owner": "java test harness", + "tags": ["default"] + } + } + """, received.take(), true); + JSONAssert.assertEquals(""" + { + "command": "power_on_job_boards", + "args": [123], + "kwargs": {} + } + """, received.take(), true); + JSONAssert.assertEquals(""" + { + "command": "get_job_state", + "args": [123], + "kwargs": {} + } + """, received.take(), true); + JSONAssert.assertEquals(""" + { + "command": "notify_job", + "args": [123], + "kwargs": {} + } + """, received.take(), true); + JSONAssert.assertEquals(""" + { + "command": "get_job_state", + "args": [123], + "kwargs": {} + } + """, received.take(), true); + JSONAssert.assertEquals(""" + { + "command": "get_job_machine_info", + "args": [123], + "kwargs": {} + } + """, received.take(), true); + JSONAssert.assertEquals(""" + { + "command": "destroy_job", + "args": [123], + "kwargs": { + "reason": "abc" + } + } + """, received.take(), true); assertTrue(received.isEmpty(), "must have checked all received messages"); } @@ -134,8 +207,13 @@ private static void assertMockReceivedKeepalivesInRange( // All should have the same message sent var first = keepalives.take(); assertNotNull(first, "null in keepalive queue!"); - JSONAssert.assertEquals("{\"command\": \"job_keepalive\", " - + "\"args\": [123], \"kwargs\": {}}", first, true); + JSONAssert.assertEquals(""" + { + "command": "job_keepalive", + "args": [123], + "kwargs": {} + } + """, first, true); while (!keepalives.isEmpty()) { JSONAssert.assertEquals(first, keepalives.take(), true); } diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestMockClient.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestMockClient.java index 6bebb0d5cf..62c48aec82 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestMockClient.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/TestMockClient.java @@ -117,7 +117,7 @@ void testListMachines() boolean previous = client.isActual(); var whereis1 = client.whereIs(machineName, coords, timeout); var whereis2 = client.whereIs(machineName, physical, timeout); - var chip = whereis1.getChip(); + var chip = whereis1.chip(); var whereis3 = client.whereIs(machineName, chip, timeout); // check only work if all real or all mock if (previous == client.isActual()) { @@ -169,7 +169,7 @@ void testJob() throws IOException, SpallocServerException, Exception { assertEquals("Spin24b-223", machineName); } var connections = machineInfo.getConnections(); - var hostName = connections.get(0).getHostname(); + var hostName = connections.get(0).hostname(); if (client.isActual()) { InetAddress.getAllByName(hostName); } else { @@ -198,13 +198,13 @@ void testJob() throws IOException, SpallocServerException, Exception { assertTrue(state.getPower()); var chip = new ChipLocation(1, 1); var whereis = client.whereIs(jobId, chip, timeout); - assertEquals(chip, whereis.getJobChip()); - assertEquals(jobId, whereis.getJobId()); + assertEquals(chip, whereis.jobChip()); + assertEquals(jobId, whereis.jobId()); if (client.isActual()) { - assertNotNull(whereis.getBoardChip()); + assertNotNull(whereis.boardChip()); } else { - assertEquals(MockConnectedClient.MOCK_ID, whereis.getJobId()); - assertEquals(chip, whereis.getBoardChip()); + assertEquals(MockConnectedClient.MOCK_ID, whereis.jobId()); + assertEquals(chip, whereis.boardChip()); } client.destroyJob(jobId, "Test finished", timeout); state = client.getJobState(jobId, timeout); @@ -220,7 +220,7 @@ void testVersion() throws IOException, SpallocServerException, Exception { var version = client.version(timeout); if (client.isActual()) { // TODO: Something here! - assertThat("version is meaningful", version.majorVersion, + assertThat("version is meaningful", version.majorVersion(), greaterThan(0)); } else { assertNotNull(version); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestBoardCoordinates.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestBoardCoordinates.java index 3355aef9f4..349939fc16 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestBoardCoordinates.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestBoardCoordinates.java @@ -34,9 +34,9 @@ void testFromJson() throws IOException { var json = "[2, 4, 6]"; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, BoardCoordinates.class); - assertEquals(2, fromJson.getX()); - assertEquals(4, fromJson.getY()); - assertEquals(6, fromJson.getZ()); + assertEquals(2, fromJson.x()); + assertEquals(4, fromJson.y()); + assertEquals(6, fromJson.z()); var direct = new BoardCoordinates(2, 4, 6); assertEquals(direct, fromJson); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestBoardPhysicalCoordinates.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestBoardPhysicalCoordinates.java index 389cf671bb..bbc908f304 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestBoardPhysicalCoordinates.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestBoardPhysicalCoordinates.java @@ -34,9 +34,9 @@ void testFromJson() throws IOException { var json = "[2, 4, 6]"; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, BoardPhysicalCoordinates.class); - assertEquals(2, fromJson.getCabinet()); - assertEquals(4, fromJson.getFrame()); - assertEquals(6, fromJson.getBoard()); + assertEquals(2, fromJson.cabinet()); + assertEquals(4, fromJson.frame()); + assertEquals(6, fromJson.board()); var direct = new BoardPhysicalCoordinates(2, 4, 6); assertEquals(direct, fromJson); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestConnection.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestConnection.java index c44f0d2672..9977e2b664 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestConnection.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestConnection.java @@ -36,8 +36,8 @@ void testFromJson() throws IOException { var json = "[[2,4],\"6.8.10.12\"]"; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, Connection.class); - assertEquals(new ChipLocation(2, 4), fromJson.getChip()); - assertEquals("6.8.10.12", fromJson.getHostname()); + assertEquals(new ChipLocation(2, 4), fromJson.chip()); + assertEquals("6.8.10.12", fromJson.hostname()); var direct = new Connection(new ChipLocation(2, 4), "6.8.10.12"); assertEquals(direct, fromJson); @@ -50,8 +50,8 @@ void testNulls() throws IOException { var json = "[null,null]"; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, Connection.class); - assertNull(fromJson.getChip()); - assertNull(fromJson.getHostname()); + assertNull(fromJson.chip()); + assertNull(fromJson.hostname()); var direct = new Connection(null, null); assertEquals(direct, fromJson); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestJobDescription.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestJobDescription.java index 2b3340ca11..8252c33a7d 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestJobDescription.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestJobDescription.java @@ -16,10 +16,10 @@ package uk.ac.manchester.spinnaker.spalloc.messages; import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; +import java.util.List; import org.hamcrest.collection.IsMapContaining; import org.junit.jupiter.api.Test; @@ -33,24 +33,30 @@ public class TestJobDescription { @Test void testOneArg() throws IOException { - var json = "{\"job_id\":12345," - + "\"owner\":\"someone@manchester.ac.uk\"," - + "\"start_time\":1.537284307847865E9," - + "\"keepalive\":45.0," - + "\"state\":3," - + "\"power\":true," - + "\"args\":[1]," - + "\"kwargs\":{" + ( - "\"tags\":null," - + "\"max_dead_boards\":0," - + "\"machine\":null," - + "\"min_ratio\":0.333," - + "\"max_dead_links\":null," - + "\"require_torus\":false" - ) + "}," - + "\"allocated_machine_name\":\"Spin24b-223\"," - + "\"boards\":[[1,1,2]]," - + "\"keepalivehost\":\"130.88.198.171\"}"; + var json = """ + { + "job_id": 12345, + "owner": "someone@manchester.ac.uk", + "start_time": 1.537284307847865E9, + "keepalive": 45.0, + "state": 3, + "power": true, + "args": [1], + "kwargs": { + "tags": null, + "max_dead_boards": 0, + "machine": null, + "min_ratio": 0.333, + "max_dead_links": null, + "require_torus": false + }, + "allocated_machine_name": "Spin24b-223", + "boards": [ + [1, 1, 2] + ], + "keepalivehost": "130.88.198.171" + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, JobDescription.class); @@ -60,7 +66,7 @@ void testOneArg() throws IOException { assertEquals(45, fromJson.getKeepAlive()); assertEquals(State.values()[3], fromJson.getState()); assertEquals(true, fromJson.getPower()); - assertThat(fromJson.getArgs(), contains(1)); + assertEquals(List.of(1), fromJson.getArgs()); var map = fromJson.getKwargs(); assertThat(map, IsMapContaining.hasEntry("tags", null)); assertThat(map, IsMapContaining.hasEntry("max_dead_boards", 0)); @@ -69,14 +75,18 @@ void testOneArg() throws IOException { assertThat(map, IsMapContaining.hasEntry("max_dead_links", null)); assertThat(map, IsMapContaining.hasEntry("require_torus", false)); assertEquals("Spin24b-223", fromJson.getMachine()); - assertThat(fromJson.getBoards(), - contains(new BoardCoordinates(1, 1, 2))); + assertEquals(List.of(new BoardCoordinates(1, 1, 2)), + fromJson.getBoards()); assertEquals("130.88.198.171", fromJson.getKeepAliveHost()); } @Test void testNulls() throws IOException { - var json = "{\"job_id\":null}"; + var json = """ + { + "job_id": null + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, JobDescription.class); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestJobMachineInfo.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestJobMachineInfo.java index ffc10b501d..083622a5a0 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestJobMachineInfo.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestJobMachineInfo.java @@ -32,14 +32,29 @@ public class TestJobMachineInfo { @Test void testFromJson() throws IOException { - var json = "{\"connections\":[[[0,0],\"10.2.225.177\"]," - + "[[4,8],\"10.2.225.145\"],[[0,12],\"10.2.225.185\"]," - + "[[8,16],\"10.2.225.121\"],[[4,20],\"10.2.225.153\"]," - + "[[8,4],\"10.2.225.113\"]]," - + "\"width\":16,\"machine_name\":\"Spin24b-001\"," - + "\"boards\":" - + "[[2,1,1],[2,1,0],[2,1,2],[2,0,2],[2,0,1],[2,0,0]]," - + "\"height\":24}"; + var json = """ + { + "connections": [ + [[0,0], "10.2.225.177"], + [[4,8], "10.2.225.145"], + [[0,12], "10.2.225.185"], + [[8,16], "10.2.225.121"], + [[4,20], "10.2.225.153"], + [[8,4], "10.2.225.113"] + ], + "width": 16, + "machine_name": "Spin24b-001", + "boards": [ + [2,1,1], + [2,1,0], + [2,1,2], + [2,0,2], + [2,0,1], + [2,0,0] + ], + "height": 24 + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, JobMachineInfo.class); assertEquals(6, fromJson.getConnections().size()); @@ -51,9 +66,13 @@ void testFromJson() throws IOException { @Test void testNullJson() throws IOException { - var json = "{\"connections\":null," - + "\"machine_name\":null," - + "\"boards\":null}"; + var json = """ + { + "connections": null, + "machine_name": null, + "boards": null + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, JobMachineInfo.class); assertEquals(0, fromJson.getConnections().size()); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestMachine.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestMachine.java index a4c62c6106..5cc43c5762 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestMachine.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestMachine.java @@ -33,10 +33,22 @@ public class TestMachine { @Test void testFromJson() throws IOException { - var json = "{\"name\":\"power-monitor\"," - + "\"tags\":[\"power-monitor\",\"machine-room\"]," - + "\"width\":1,\"height\":1," - + "\"dead_boards\":[[0,0,1],[0,0,2]],\"dead_links\":[]}"; + var json = """ + { + "name": "power-monitor", + "tags": [ + "power-monitor", + "machine-room" + ], + "width": 1, + "height":1, + "dead_boards":[ + [0,0,1], + [0,0,2] + ], + "dead_links": [] + }" + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, Machine.class); assertEquals("power-monitor", fromJson.getName()); @@ -52,11 +64,25 @@ void testFromJson() throws IOException { @Test void testAssumedDeadLinks() throws IOException { - var json = "{\"name\":\"power-monitor\"," - + "\"tags\":[\"power-monitor\",\"machine-room\"]," - + "\"width\":1,\"height\":1," - + "\"dead_boards\":[[1,2,3],[4,5,6]]," - + "\"dead_links\":[[7,8,9,10],[11,12,13,14]]}"; + var json = """ + { + "name": "power-monitor", + "tags": [ + "power-monitor", + "machine-room" + ], + "width": 1, + "height": 1, + "dead_boards": [ + [1,2,3], + [4,5,6] + ], + "dead_links": [ + [7,8,9,10], + [11,12,13,14] + ] + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, Machine.class); assertEquals("power-monitor", fromJson.getName()); @@ -67,14 +93,18 @@ void testAssumedDeadLinks() throws IOException { assertThat(fromJson.getDeadBoards(), contains( new BoardCoordinates(1, 2, 3), new BoardCoordinates(4, 5, 6))); assertEquals(2, fromJson.getDeadLinks().size()); - assertEquals(7, fromJson.getDeadLinks().get(0).getX()); - assertEquals(14, fromJson.getDeadLinks().get(1).getLink()); + assertEquals(7, fromJson.getDeadLinks().get(0).x()); + assertEquals(14, fromJson.getDeadLinks().get(1).link()); assertNotNull(fromJson.toString()); } @Test void testNullJson() throws IOException { - var json = "{\"name\":null}"; + var json = """ + { + "name": null + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, Machine.class); assertNull(fromJson.getName()); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestState.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestState.java index ba8ebea0a6..b441c79333 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestState.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestState.java @@ -31,12 +31,16 @@ public class TestState { @Test void testFromJson() throws IOException { - var json = "{\"state\":2," - + "\"power\":true," - + "\"keepalive\":60.0," - + "\"reason\":null," - + "\"start_time\":1.125," - + "\"keepalivehost\":\"86.82.216.229\"}"; + var json = """ + { + "state": 2, + "power": true, + "keepalive": 60.0, + "reason": null, + "start_time": 1.125, + "keepalivehost": "86.82.216.229" + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, JobState.class); assertEquals(State.POWER, fromJson.getState()); @@ -50,7 +54,11 @@ void testFromJson() throws IOException { @Test void testNullJson() throws IOException { - var json = "{\"reason\":null}"; + var json = """ + { + "reason": null + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, JobState.class); assertNull(fromJson.getState()); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestWhereIs.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestWhereIs.java index 05588fe0c1..4bd64cb121 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestWhereIs.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/spalloc/messages/TestWhereIs.java @@ -38,18 +38,26 @@ void testFromJson() throws IOException { var logical = new BoardCoordinates(5, 6, 7); var physical = new BoardPhysicalCoordinates(10, 11, 12); - var json = "{\"job_chip\":[1,2],\"job_id\":666,\"chip\":[3,4]," - + "\"logical\":[5,6,7],\"machine\":\"Spin24b-001\"," - + "\"board_chip\":[8,9],\"physical\":[10,11,12]}"; + var json = """ + { + "job_chip": [1,2], + "job_id": 666, + "chip": [3,4], + "logical": [5,6,7], + "machine": "Spin24b-001", + "board_chip": [8,9], + "physical": [10,11,12] + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, WhereIs.class); - assertEquals(jobChip, fromJson.getJobChip()); - assertEquals(666, fromJson.getJobId()); - assertEquals(chip, fromJson.getChip()); - assertEquals(logical, fromJson.getLogical()); - assertEquals("Spin24b-001", fromJson.getMachine()); - assertEquals(boardChip, fromJson.getBoardChip()); - var physical2 = fromJson.getPhysical(); + assertEquals(jobChip, fromJson.jobChip()); + assertEquals(666, fromJson.jobId()); + assertEquals(chip, fromJson.chip()); + assertEquals(logical, fromJson.logical()); + assertEquals("Spin24b-001", fromJson.machine()); + assertEquals(boardChip, fromJson.boardChip()); + var physical2 = fromJson.physical(); assertEquals(physical, physical2); var direct = new WhereIs(jobChip, 666, chip, logical, "Spin24b-001", @@ -66,18 +74,26 @@ void testBug() throws IOException { var logical = new BoardCoordinates(0, 0, 1); var physical = new BoardPhysicalCoordinates(0, 0, 8); - var json = "{\"job_chip\":null,\"job_id\":null,\"chip\":[8,4]," - + "\"logical\":[0,0,1],\"machine\":\"Spin24b-001\"," - + "\"board_chip\":[0,0],\"physical\":[0,0,8]}"; + var json = """ + { + "job_chip": null, + "job_id": null, + "chip": [8,4], + "logical": [0,0,1], + "machine": "Spin24b-001", + "board_chip": [0,0], + "physical": [0,0,8] + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, WhereIs.class); - assertNull(fromJson.getJobChip()); - assertNull(fromJson.getJobId()); - assertEquals(chip, fromJson.getChip()); - assertEquals(logical, fromJson.getLogical()); - assertEquals("Spin24b-001", fromJson.getMachine()); - assertEquals(boardChip, fromJson.getBoardChip()); - assertEquals(physical, fromJson.getPhysical()); + assertNull(fromJson.jobChip()); + assertNull(fromJson.jobId()); + assertEquals(chip, fromJson.chip()); + assertEquals(logical, fromJson.logical()); + assertEquals("Spin24b-001", fromJson.machine()); + assertEquals(boardChip, fromJson.boardChip()); + assertEquals(physical, fromJson.physical()); var direct = new WhereIs(null, null, chip, logical, "Spin24b-001", boardChip, physical); @@ -88,19 +104,26 @@ void testBug() throws IOException { @Test void testNulls() throws IOException { - - var json = "{\"job_chip\":null,\"job_id\":null,\"chip\":null," - + "\"logical\":null,\"machine\":null,\"board_chip\":null," - + "\"physical\":null}"; + var json = """ + { + "job_chip": null, + "job_id": null, + "chip": null, + "logical": null, + "machine": null, + "board_chip": null, + "physical": null + } + """; var mapper = SpallocClient.createMapper(); var fromJson = mapper.readValue(json, WhereIs.class); - assertNull(fromJson.getJobChip()); - assertNull(fromJson.getJobId()); - assertNull(fromJson.getChip()); - assertNull(fromJson.getLogical()); - assertNull(fromJson.getMachine()); - assertNull(fromJson.getBoardChip()); - assertNull(fromJson.getPhysical()); + assertNull(fromJson.jobChip()); + assertNull(fromJson.jobId()); + assertNull(fromJson.chip()); + assertNull(fromJson.logical()); + assertNull(fromJson.machine()); + assertNull(fromJson.boardChip()); + assertNull(fromJson.physical()); var direct = new WhereIs(null, null, null, null, null, null, null); assertEquals(direct, fromJson); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/transceiver/TestTransceiver.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/transceiver/TestTransceiver.java index a2692a171d..7194a4597a 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/transceiver/TestTransceiver.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/transceiver/TestTransceiver.java @@ -128,15 +128,15 @@ void testRetrievingMachineDetails() throws Exception { try (var txrx = new Transceiver(FIVE, connections, null, null, null, null, null)) { if (boardConfig.boardVersion.isFourChip) { - assertEquals(2, txrx.getMachineDimensions().width); - assertEquals(2, txrx.getMachineDimensions().height); + assertEquals(2, txrx.getMachineDimensions().width()); + assertEquals(2, txrx.getMachineDimensions().height()); } else if (boardConfig.boardVersion.isFourtyeightChip) { - assertEquals(8, txrx.getMachineDimensions().width); - assertEquals(8, txrx.getMachineDimensions().height); + assertEquals(8, txrx.getMachineDimensions().width()); + assertEquals(8, txrx.getMachineDimensions().height()); } else { var size = txrx.getMachineDimensions(); - fail(format("Unknown board with size %dx%d", size.width, - size.height)); + fail(format("Unknown board with size %dx%d", size.width(), + size.height())); } assertTrue(txrx.isConnected()); assertNotNull(txrx.getScampVersion()); diff --git a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/transceiver/TransceiverITCase.java b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/transceiver/TransceiverITCase.java index 3be4b1d9df..6ed52e52ee 100644 --- a/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/transceiver/TransceiverITCase.java +++ b/SpiNNaker-comms/src/test/java/uk/ac/manchester/spinnaker/transceiver/TransceiverITCase.java @@ -18,8 +18,6 @@ import static java.lang.Math.random; import static java.lang.Thread.sleep; import static java.net.InetAddress.getLocalHost; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static java.util.Collections.sort; import static java.util.stream.Collectors.toSet; import static java.util.stream.IntStream.range; @@ -43,6 +41,7 @@ import static uk.ac.manchester.spinnaker.messages.model.RouterDiagnostics.RouterRegister.LOC_PP; import static uk.ac.manchester.spinnaker.messages.model.Signal.STOP; import static uk.ac.manchester.spinnaker.transceiver.CommonMemoryLocations.BUFFERED_SDRAM_START; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.io.File; import java.util.ArrayList; @@ -200,7 +199,7 @@ private void retrieveDetails(Transceiver txrx) throws Exception { } private void readWrite(Transceiver txrx) throws Exception { - var writeData = allocate(1000); + var writeData = alloc(1000); while (writeData.hasRemaining()) { writeData.put((byte) (random() * 256)); } @@ -398,7 +397,7 @@ public void testTransceiver() throws Exception { */ long longVal = 123456789123456789L; int intVal = 123456789; - var longData = allocate(8).order(LITTLE_ENDIAN); + var longData = alloc(8); longData.putLong(longVal).flip(); section("Test reading/writing blobs", () -> { diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/CommandLineInterface.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/CommandLineInterface.java index 01f5602380..53df41546b 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/CommandLineInterface.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/CommandLineInterface.java @@ -16,14 +16,13 @@ package uk.ac.manchester.spinnaker.front_end; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toUnmodifiableList; import org.slf4j.Logger; import static org.slf4j.LoggerFactory.getLogger; import static picocli.CommandLine.ExitCode.USAGE; import static uk.ac.manchester.spinnaker.alloc.client.SpallocClientFactory.getJobFromProxyInfo; import static uk.ac.manchester.spinnaker.front_end.CommandDescriptions.DOWNLOAD_DESC; import static uk.ac.manchester.spinnaker.front_end.CommandDescriptions.DSE_APP_DESC; -import static uk.ac.manchester.spinnaker.front_end.CommandDescriptions.DSE_DESC; import static uk.ac.manchester.spinnaker.front_end.CommandDescriptions.DSE_MON_DESC; import static uk.ac.manchester.spinnaker.front_end.CommandDescriptions.DSE_SYS_DESC; import static uk.ac.manchester.spinnaker.front_end.CommandDescriptions.GATHER_DESC; @@ -81,7 +80,7 @@ import uk.ac.manchester.spinnaker.storage.BufferManagerStorage; import uk.ac.manchester.spinnaker.storage.DSEDatabaseEngine; import uk.ac.manchester.spinnaker.storage.DatabaseEngine; -import uk.ac.manchester.spinnaker.storage.ProxyAwareStorage; +import uk.ac.manchester.spinnaker.storage.DatabaseAPI; import uk.ac.manchester.spinnaker.storage.StorageException; import uk.ac.manchester.spinnaker.transceiver.SpinnmanException; import uk.ac.manchester.spinnaker.transceiver.Transceiver; @@ -701,43 +700,43 @@ private static BufferManagerStorage getBufferManagerDB(File dbFile) { } private static SpallocClient.Job getJob( - DatabaseEngine databaseEngine) + DatabaseEngine databaseEngine) throws StorageException, IOException { return getJob(databaseEngine.getStorageInterface()); } - private static SpallocClient.Job getJob(ProxyAwareStorage storage) + private static SpallocClient.Job getJob(DatabaseAPI storage) throws StorageException, IOException { return getJobFromProxyInfo(storage.getProxyInformation()); } + @MustBeClosed + private static TransceiverInterface makeTxrx(Machine machine, + SpallocClient.Job job) + throws IOException, SpinnmanException, InterruptedException { + if (job != null) { + return job.getTransceiver(); + } + // No job; must be a direct connection + var conns = machine.ethernetConnectedChips().stream() + .map(chip -> new ConnectionDescriptor(chip.ipAddress, + SCP_SCAMP_PORT, chip.asChipLocation())) + .collect(toUnmodifiableList()); + return Transceiver.makeWithDescriptors(machine.version, conns); + } + @MustBeClosed @SuppressWarnings("MustBeClosed") private static TransceiverInterface getTransceiver(Machine machine, SpallocClient.Job job) throws IOException, SpinnmanException, InterruptedException { - final TransceiverInterface txrx; - if (job == null) { - // No job; must be a direct connection - txrx = Transceiver.makeWithDescriptors( - machine.version, generateScampConnections(machine)); - } else { - txrx = job.getTransceiver(); - } + var txrx = makeTxrx(machine, job); var scpSelector = txrx.getScampConnectionSelector(); - if (scpSelector instanceof MachineAware) { - ((MachineAware) scpSelector).setMachine(machine); + if (scpSelector instanceof MachineAware ma) { + ma.setMachine(machine); } return txrx; } - - private static List generateScampConnections( - Machine machine) { - return machine.ethernetConnectedChips().stream() - .map(chip -> new ConnectionDescriptor(chip.ipAddress, - SCP_SCAMP_PORT, chip.asChipLocation())) - .collect(toList()); - } } /** diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/DebuggingUtils.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/DebuggingUtils.java new file mode 100644 index 0000000000..ead9067eeb --- /dev/null +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/DebuggingUtils.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019-2022 The University of Manchester + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package uk.ac.manchester.spinnaker.front_end; + +import static difflib.DiffUtils.diff; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.sliceUp; +import static java.util.stream.Collectors.toList; + +import java.nio.ByteBuffer; +import java.util.List; + +import org.slf4j.Logger; + +import difflib.Chunk; +import uk.ac.manchester.spinnaker.utils.MathUtils; + +/** + * Utilities for debugging. + */ +public abstract class DebuggingUtils { + private DebuggingUtils() { + } + + /** + * Compare two buffers and write a description of how they differ to the + * given log (if they actually differ). + * + * @param original + * The data to compare against. The ground truth. + * @param downloaded + * The data that is being checked for differences. + * @param log + * Where to write messages on differences being found. + */ + public static void compareBuffers(ByteBuffer original, + ByteBuffer downloaded, Logger log) { + for (int i = 0; i < original.remaining(); i++) { + if (original.get(i) != downloaded.get(i)) { + log.error("downloaded buffer contents different"); + for (var delta : diff(list(original), list(downloaded)) + .getDeltas()) { + switch (delta.getType()) { + case CHANGE -> { + var changeFrom = delta.getOriginal(); + var changeTo = delta.getRevised(); + log.warn( + "swapped {} bytes (SCP) for {} (gather) " + + "at {}->{}", + changeFrom.getLines().size(), + changeTo.getLines().size(), + changeFrom.getPosition(), + changeTo.getPosition()); + log.info("change {} -> {}", describeChunk(changeFrom), + describeChunk(changeTo)); + } + case DELETE -> { + var delete = delta.getOriginal(); + log.warn("gather deleted {} bytes at {}", + delete.getLines().size(), delete.getPosition()); + log.info("delete {}", describeChunk(delete)); + } + default /* INSERT */ -> { + var insert = delta.getRevised(); + log.warn("gather inserted {} bytes at {}", + insert.getLines().size(), insert.getPosition()); + log.info("insert {}", describeChunk(insert)); + } + } + } + break; + } + } + } + + private static List list(ByteBuffer buffer) { + return sliceUp(buffer, 1).map(ByteBuffer::get).toList(); + } + + private static List describeChunk(Chunk chunk) { + return chunk.getLines().stream().map(MathUtils::hexbyte) + .collect(toList()); + } +} diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/NoDropPacketContext.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/NoDropPacketContext.java index afe22f0ad5..874467156b 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/NoDropPacketContext.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/NoDropPacketContext.java @@ -45,7 +45,7 @@ * @author Donal Fellows * @author Alan Stokes */ -public class NoDropPacketContext implements AutoCloseable { +public final class NoDropPacketContext implements AutoCloseable { private static final Logger log = getLogger(NoDropPacketContext.class); private ReinjectionStatus lastStatus; diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/BufferedReceivingData.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/BufferedReceivingData.java index 88ccd2a993..edc8153e57 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/BufferedReceivingData.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/BufferedReceivingData.java @@ -121,11 +121,11 @@ public RecordingRegion getRecordingRegion(RegionLocation location) { throw new IllegalArgumentException( "no regions known for " + coreLocation); } - if (value.size() < location.region || location.region < 0) { + if (value.size() < location.region() || location.region() < 0) { throw new IllegalArgumentException( "no region known for " + location); } - return value.get(location.region); + return value.get(location.region()); } /** @@ -143,11 +143,11 @@ public void storeDataInRegionBuffer(RegionLocation location, ByteBuffer data) throws StorageException { if (log.isInfoEnabled()) { log.info("retrieved {} bytes from region {} of {}", - data.remaining(), location.region, + data.remaining(), location.region(), location.asCoreLocation()); } storage.appendRecordingContents( - new Region(location, location.region, NULL, 0), data); + new Region(location, location.region(), NULL, 0), data); } /** diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/ClearMessage.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/ClearMessage.java index 2f154e14f7..eddd5da191 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/ClearMessage.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/ClearMessage.java @@ -15,11 +15,10 @@ */ package uk.ac.manchester.spinnaker.front_end.download; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.front_end.download.GatherProtocolMessage.ID.CLEAR_TRANSMISSIONS; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; import static uk.ac.manchester.spinnaker.messages.sdp.SDPPort.EXTRA_MONITOR_CORE_DATA_SPEED_UP; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.nio.ByteBuffer; @@ -45,7 +44,7 @@ public final class ClearMessage extends GatherProtocolMessage { * @return The created message. */ static ClearMessage create(HasCoreLocation destination, int transactionId) { - var payload = allocate(NUM_WORDS * WORD_SIZE).order(LITTLE_ENDIAN); + var payload = alloc(NUM_WORDS * WORD_SIZE); payload.putInt(CLEAR_TRANSMISSIONS.value); payload.putInt(transactionId); payload.flip(); diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DataGatherer.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DataGatherer.java index a736a57ef6..443d3e087a 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DataGatherer.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DataGatherer.java @@ -15,19 +15,17 @@ */ package uk.ac.manchester.spinnaker.front_end.download; -import static difflib.DiffUtils.diff; import static java.lang.String.format; import static java.lang.System.getProperty; import static java.lang.Thread.sleep; import static java.nio.ByteBuffer.allocate; -import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toUnmodifiableList; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.front_end.Constants.PARALLEL_SIZE; +import static uk.ac.manchester.spinnaker.front_end.DebuggingUtils.compareBuffers; import static uk.ac.manchester.spinnaker.front_end.download.MissingSequenceNumbersMessage.createMessages; import static uk.ac.manchester.spinnaker.messages.Constants.SDP_PAYLOAD_WORDS; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; -import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.sliceUp; import static uk.ac.manchester.spinnaker.utils.MathUtils.ceildiv; import java.io.IOException; @@ -46,10 +44,6 @@ import com.google.errorprone.annotations.MustBeClosed; import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; -import difflib.ChangeDelta; -import difflib.Chunk; -import difflib.DeleteDelta; -import difflib.InsertDelta; import uk.ac.manchester.spinnaker.connections.MostDirectConnectionSelector; import uk.ac.manchester.spinnaker.front_end.BasicExecutor; import uk.ac.manchester.spinnaker.front_end.BasicExecutor.SimpleCallable; @@ -66,7 +60,6 @@ import uk.ac.manchester.spinnaker.storage.StorageException; import uk.ac.manchester.spinnaker.transceiver.ProcessException; import uk.ac.manchester.spinnaker.transceiver.TransceiverInterface; -import uk.ac.manchester.spinnaker.utils.MathUtils; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; /** @@ -75,8 +68,9 @@ * @author Donal Fellows * @author Alan Stokes */ -public abstract class DataGatherer extends BoardLocalSupport - implements AutoCloseable { +@SuppressWarnings("deprecation") +public abstract sealed class DataGatherer extends BoardLocalSupport implements + AutoCloseable permits DirectDataGatherer, RecordingRegionDataGatherer { /** * Logger for the gatherer. */ @@ -201,30 +195,20 @@ public int gather(List gatherers) throws IOException, } /** - * Trivial POJO holding the pairing of monitor and list of lists of memory + * Trivial record holding the pairing of monitor and list of lists of memory * blocks. * * @author Donal Fellows + * @param monitor + * Monitor that is used to download the regions. + * @param regions + * List of information about where to download. The inner + * sub-lists are ordered, and are either one or two items long to + * represent what pieces of memory should really be downloaded. + * The outer list could theoretically be done in any order... but + * needs to be processed single-threaded anyway. */ - private static final class WorkItems { - /** - * Monitor that is used to download the regions. - */ - private final Monitor monitor; - - /** - * List of information about where to download. The inner sub-lists are - * ordered, and are either one or two items long to represent what - * pieces of memory should really be downloaded. The outer list could - * theoretically be done in any order... but needs to be processed - * single-threaded anyway. - */ - private final List> regions; - - WorkItems(Monitor m, List> region) { - this.monitor = m; - this.regions = region; - } + private record WorkItems(Monitor monitor, List> regions) { } /** @@ -258,7 +242,7 @@ private Map> discoverActualWork( for (var p : m.getPlacements()) { var regions = new ArrayList>(); - for (int id : p.getVertex().getRecordedRegionIds()) { + for (int id : p.vertex().recordedRegionIds()) { var r = getRegion(p, id); if (!r.isEmpty()) { regions.add(r); @@ -369,14 +353,14 @@ private void fastDownload(List work, log.info("processing fast downloads for {}", conn.getChip()); var dl = new Downloader(conn); for (var item : work) { - for (var regionsOnCore : item.regions) { + for (var regionsOnCore : item.regions()) { /* * Once there's something too small, all subsequent * retrieves for that recording region have to be done the * same way to get the data in the DB in the right order. */ for (var region : regionsOnCore) { - var data = dl.doDownload(item.monitor, region); + var data = dl.doDownload(item.monitor(), region); if (SPINNAKER_COMPARE_DOWNLOAD != null) { compareDownloadWithSCP(region, data); } @@ -389,10 +373,7 @@ private void fastDownload(List work, private void sanityCheck(List gatherers) { var sel = txrx.getScampConnectionSelector(); - MostDirectConnectionSelector s = null; - if (sel instanceof MostDirectConnectionSelector) { - s = (MostDirectConnectionSelector) sel; - } + var s = (sel instanceof MostDirectConnectionSelector ss ? ss : null); // Sanity check the inputs for (var g : gatherers) { @@ -417,45 +398,7 @@ private void compareDownloadWithSCP(Region r, ByteBuffer data) log.error("different buffer sizes: {} with gatherer, {} with SCP", data.remaining(), data2.remaining()); } - for (int i = 0; i < data.remaining(); i++) { - if (data.get(i) != data2.get(i)) { - log.error("downloaded buffer contents different"); - for (var delta : diff(list(data2), list(data)).getDeltas()) { - if (delta instanceof ChangeDelta) { - var delete = delta.getOriginal(); - var insert = delta.getRevised(); - log.warn( - "swapped {} bytes (SCP) for {} (gather) " - + "at {}->{}", - delete.getLines().size(), - insert.getLines().size(), delete.getPosition(), - insert.getPosition()); - log.info("change {} -> {}", describeChunk(delete), - describeChunk(insert)); - } else if (delta instanceof DeleteDelta) { - var delete = delta.getOriginal(); - log.warn("gather deleted {} bytes at {}", - delete.getLines().size(), delete.getPosition()); - log.info("delete {}", describeChunk(delete)); - } else if (delta instanceof InsertDelta) { - var insert = delta.getRevised(); - log.warn("gather inserted {} bytes at {}", - insert.getLines().size(), insert.getPosition()); - log.info("insert {}", describeChunk(insert)); - } - } - break; - } - } - } - - private static List list(ByteBuffer buffer) { - return sliceUp(buffer, 1).map(ByteBuffer::get).toList(); - } - - private static List describeChunk(Chunk chunk) { - return chunk.getLines().stream().map(MathUtils::hexbyte) - .collect(toList()); + compareBuffers(data, data2, log); } /** diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DataReceiver.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DataReceiver.java index ea5167fa2b..fab5c9fcb1 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DataReceiver.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DataReceiver.java @@ -138,8 +138,8 @@ public void getDataForPlacements(List placements) // get data try (var c = new BoardLocal(placements.get(0))) { for (var placement : placements) { - for (int recordingRegionId : placement.getVertex() - .getRecordedRegionIds()) { + for (int recordingRegionId : placement.vertex() + .recordedRegionIds()) { getDataForPlacement(placement, recordingRegionId); } } diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DirectDataGatherer.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DirectDataGatherer.java index b99e1883ed..0352999eb2 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DirectDataGatherer.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/DirectDataGatherer.java @@ -49,7 +49,7 @@ * @see RecordingRegionDataGatherer */ @Deprecated -public class DirectDataGatherer extends DataGatherer { +public final class DirectDataGatherer extends DataGatherer { /** The number of memory regions in the DSE model. */ private static final int MAX_MEM_REGIONS = 16; @@ -112,9 +112,9 @@ private IntBuffer getCoreRegionTable(CoreLocation core, Vertex vertex) map = coreTableCache.computeIfAbsent(core, __ -> new HashMap<>()); } // Individual cores are only ever handled from one thread - var buffer = map.get(vertex.getBase()); + var buffer = map.get(vertex.base()); if (buffer == null) { - buffer = txrx.readMemory(core, vertex.getBase(), + buffer = txrx.readMemory(core, vertex.base(), WORD_SIZE * (MAX_MEM_REGIONS + 2)); int word = buffer.getInt(); if (word != APPDATA_MAGIC_NUM) { @@ -126,7 +126,7 @@ private IntBuffer getCoreRegionTable(CoreLocation core, Vertex vertex) throw new IllegalStateException( format("unexpected DSE version: %08x", word)); } - map.put(vertex.getBase(), buffer); + map.put(vertex.base(), buffer); } return buffer.asIntBuffer(); } @@ -135,7 +135,7 @@ private IntBuffer getCoreRegionTable(CoreLocation core, Vertex vertex) protected List getRegion(Placement placement, int regionID) throws IOException, ProcessException, InterruptedException { var b = getCoreRegionTable(placement.asCoreLocation(), - placement.getVertex()); + placement.vertex()); // TODO This is wrong because of shared regions! int size = b.get(regionID + 1) - b.get(regionID); return List.of(new Region(placement, regionID, diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/GatherProtocolMessage.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/GatherProtocolMessage.java index 22b4bf1aab..a927d6c6db 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/GatherProtocolMessage.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/GatherProtocolMessage.java @@ -31,7 +31,9 @@ * * @author Donal Fellows */ -public abstract class GatherProtocolMessage extends SDPMessage { +public abstract sealed class GatherProtocolMessage extends SDPMessage + permits ClearMessage, StartSendingMessage, + MissingSequenceNumbersMessage { /** * Create a protocol message. * diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/MissingSequenceNumbersMessage.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/MissingSequenceNumbersMessage.java index 26585338c5..af7b069d90 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/MissingSequenceNumbersMessage.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/MissingSequenceNumbersMessage.java @@ -16,14 +16,13 @@ package uk.ac.manchester.spinnaker.front_end.download; import static java.lang.Math.min; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.front_end.Constants.NEXT_MESSAGES_COUNT; import static uk.ac.manchester.spinnaker.front_end.download.GatherProtocolMessage.ID.NEXT_MISSING_SEQS; import static uk.ac.manchester.spinnaker.front_end.download.GatherProtocolMessage.ID.START_MISSING_SEQS; import static uk.ac.manchester.spinnaker.messages.Constants.SDP_PAYLOAD_WORDS; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; import static uk.ac.manchester.spinnaker.messages.sdp.SDPPort.EXTRA_MONITOR_CORE_DATA_SPEED_UP; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import static uk.ac.manchester.spinnaker.utils.MathUtils.ceildiv; import java.nio.ByteBuffer; @@ -87,7 +86,7 @@ private static int computeNumberOfPackets(int numSequenceNumbers) { */ private static ByteBuffer allocateWords(int numDataWords, int overhead) { int numWords = min(SDP_PAYLOAD_WORDS, numDataWords + overhead); - return allocate(numWords * WORD_SIZE).order(LITTLE_ENDIAN); + return alloc(numWords * WORD_SIZE); } /** diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/RecordingRegion.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/RecordingRegion.java index 202347d0fd..c3f0bdccfc 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/RecordingRegion.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/RecordingRegion.java @@ -116,7 +116,7 @@ public String toString() { public static List getRecordingRegionDescriptors( TransceiverInterface txrx, Placement placement) throws IOException, ProcessException, InterruptedException { - var recordingDataAddress = placement.getVertex().getBase(); + var recordingDataAddress = placement.vertex().base(); // Get the size of the list of recordings int nRegions = txrx.readMemory(placement.getScampCore(), recordingDataAddress, WORD_SIZE).getInt(); diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/RecordingRegionDataGatherer.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/RecordingRegionDataGatherer.java index b1c2085b8c..9f6162f358 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/RecordingRegionDataGatherer.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/RecordingRegionDataGatherer.java @@ -51,7 +51,7 @@ * * @author Donal Fellows */ -public class RecordingRegionDataGatherer extends DataGatherer { +public final class RecordingRegionDataGatherer extends DataGatherer { /** * How long a termination delay has to be to be worth reporting, in * milliseconds. diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/StartSendingMessage.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/StartSendingMessage.java index 2c76b2aa20..a3668925c1 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/StartSendingMessage.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/StartSendingMessage.java @@ -15,11 +15,10 @@ */ package uk.ac.manchester.spinnaker.front_end.download; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.front_end.download.GatherProtocolMessage.ID.START_SENDING_DATA; import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; import static uk.ac.manchester.spinnaker.messages.sdp.SDPPort.EXTRA_MONITOR_CORE_DATA_SPEED_UP; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import java.nio.ByteBuffer; @@ -48,10 +47,10 @@ public final class StartSendingMessage extends GatherProtocolMessage { */ static StartSendingMessage create(HasCoreLocation destination, MemoryLocation address, int length, int transactionId) { - var payload = allocate(NUM_WORDS * WORD_SIZE).order(LITTLE_ENDIAN); + var payload = alloc(NUM_WORDS * WORD_SIZE); payload.putInt(START_SENDING_DATA.value); payload.putInt(transactionId); - payload.putInt(address.address); + payload.putInt(address.address()); payload.putInt(length); payload.flip(); return new StartSendingMessage(destination, payload); diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Gather.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Gather.java index 68c7624eda..c761b7820c 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Gather.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Gather.java @@ -21,12 +21,11 @@ import java.io.IOException; import java.util.List; -import javax.validation.Valid; - import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.HasCoreLocation; import uk.ac.manchester.spinnaker.machine.ValidP; import uk.ac.manchester.spinnaker.machine.ValidX; diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Monitor.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Monitor.java index f1dade6304..c72cb65a29 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Monitor.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Monitor.java @@ -21,12 +21,11 @@ import java.io.IOException; import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.HasCoreLocation; import uk.ac.manchester.spinnaker.machine.ValidP; import uk.ac.manchester.spinnaker.machine.ValidX; diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Placement.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Placement.java index 49c48513da..95377ecff1 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Placement.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Placement.java @@ -32,9 +32,22 @@ * Vertex placement information. * * @author Christian-B + * @param x + * The X coordinate of the core this vertex is placed on. + * @param y + * The Y coordinate of the core this vertex is placed on. + * @param p + * The processor ID of the core this vertex is placed on. + * @param vertex + * Vertex recording region information. */ @JsonFormat(shape = OBJECT) -public class Placement implements HasCoreLocation { +public record Placement( + @JsonProperty(value = "x", required = true) @ValidX int x, + @JsonProperty(value = "y", required = true) @ValidY int y, + @ValidP @JsonProperty(value = "p", required = true) int p, + @JsonProperty(value = "vertex", required = true) Vertex vertex) + implements HasCoreLocation { /** * Type reference for deserializing a list of placements. */ @@ -43,45 +56,6 @@ public class Placement implements HasCoreLocation { private static final class TR extends TypeReference> { } - /** The X coordinate of the core this vertex is placed on. */ - @ValidX - private final int x; - - /** The Y coordinate of the core this vertex is placed on. */ - @ValidY - private final int y; - - /** The processor ID of the core this vertex is placed on. */ - @ValidP - private final int p; - - /** Minimal vertex info. */ - private final Vertex vertex; - - /** - * Constructor with minimum information needed. - *

- * Could be called from an unmarshaller. - * - * @param x - * Vertex X coordinate. - * @param y - * Vertex Y coordinate. - * @param p - * Vertex processor ID. - * @param vertex - * Vertex recording region information. - */ - Placement(@JsonProperty(value = "x", required = true) int x, - @JsonProperty(value = "y", required = true) int y, - @JsonProperty(value = "p", required = true) int p, - @JsonProperty(value = "vertex", required = true) Vertex vertex) { - this.x = x; - this.y = y; - this.p = p; - this.vertex = vertex; - } - @Override public int getX() { return x; @@ -96,9 +70,4 @@ public int getY() { public int getP() { return p; } - - /** @return The information about the vertex. */ - public Vertex getVertex() { - return vertex; - } } diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Vertex.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Vertex.java index 63da678050..078fb9b550 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Vertex.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/download/request/Vertex.java @@ -15,55 +15,36 @@ */ package uk.ac.manchester.spinnaker.front_end.download.request; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnore; - import static com.fasterxml.jackson.annotation.JsonFormat.Shape.OBJECT; -import javax.validation.constraints.NotNull; - +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.MemoryLocation; /** * Vertex recording region information. * * @author Christian-B + * @param label + * Label as received from Python. + * @param base + * Address at which to find recording metadata. This was allocated + * during data specification execution, and contains a description of + * all the recording regions owned by a vertex (it may well + * have several, e.g., spikes and voltages). This points to a + * structure that includes the addresses of the per-region metadata, + * and those point in turn to the actual buffers used to do the + * recording (which are circular buffers). + * @param recordedRegionIds + * The recording region IDs that have been actually recorded using + * buffering. */ @JsonFormat(shape = OBJECT) -public class Vertex { - - /** Label as received from Python. */ - private final String label; - - /** - * Address at which to find recording metadata. This was allocated during - * data specification execution, and contains a description of all - * the recording regions owned by a vertex (it may well have several, e.g., - * spikes and voltages). This points to a structure that includes the - * addresses of the per-region metadata, and those point in turn to the - * actual buffers used to do the recording (which are circular - * buffers). - */ - private final long recordingRegionBaseAddress; - - /** - * Address at which to find recording metadata. This was allocated during - * data specification execution, and contains a description of all - * the recording regions owned by a vertex (it may well have several, e.g., - * spikes and voltages). This points to a structure that includes the - * addresses of the per-region metadata, and those point in turn to the - * actual buffers used to do the recording (which are circular - * buffers). - */ - @NotNull - private final MemoryLocation base; - - /** The IDs of the regions recording. */ - @NotNull - private final int[] recordedRegionIds; - +public record Vertex(String label, @NotNull MemoryLocation base, + @NotNull int[] recordedRegionIds) { /** * Create a minimal vertex, possibly using an unmarshaller. * @@ -76,51 +57,14 @@ public class Vertex { * @param recordedRegionIds * The IDs of the regions doing recording. */ - public Vertex(@JsonProperty(value = "label", required = true) String label, - @JsonProperty(value = "recordingRegionBaseAddress", required = true) - long recordingRegionBaseAddress, - @JsonProperty(value = "recordedRegionIds", required = true) + @JsonCreator + public Vertex(// + @JsonProperty(value = "label", required = true) String label, + @JsonProperty(value = "recordingRegionBaseAddress", // + required = true) long recordingRegionBaseAddress, + @JsonProperty(value = "recordedRegionIds", required = true) // int[] recordedRegionIds) { - this.label = label; - this.recordingRegionBaseAddress = recordingRegionBaseAddress; - this.recordedRegionIds = recordedRegionIds; - this.base = new MemoryLocation(recordingRegionBaseAddress); - } - - /** - * Get the recording region IDs that have been recorded using buffering. - * - * @return The region numbers that have active recording - */ - public int[] getRecordedRegionIds() { - return recordedRegionIds; - } - - /** - * Get the recording region base address. - *

- * Unlike the python this value is cached here. - * - * @return the base address of the recording region - */ - public long getBaseAddress() { - return recordingRegionBaseAddress; - } - - /** - * Get the recording region base address. - * - * @return the base address of the recording region - */ - @JsonIgnore - public MemoryLocation getBase() { - return base; - } - - /** - * @return The label, as received from Python. - */ - public String getLabel() { - return label; + this(label, new MemoryLocation(recordingRegionBaseAddress), + recordedRegionIds); } } diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/BoardWorker.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/BoardWorker.java index 65520d7439..dda8ac748a 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/BoardWorker.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/BoardWorker.java @@ -15,16 +15,16 @@ */ package uk.ac.manchester.spinnaker.front_end.dse; -import uk.ac.manchester.spinnaker.machine.CoreLocation; +import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.front_end.Constants.CORE_DATA_SDRAM_BASE_TAG; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; +import static uk.ac.manchester.spinnaker.utils.MathUtils.ceildiv; import java.io.IOException; import java.nio.ByteBuffer; - -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import java.util.LinkedHashMap; +import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.machine.HasCoreLocation; import uk.ac.manchester.spinnaker.machine.MemoryLocation; import uk.ac.manchester.spinnaker.messages.model.AppID; @@ -33,7 +33,6 @@ import uk.ac.manchester.spinnaker.storage.StorageException; import uk.ac.manchester.spinnaker.transceiver.ProcessException; import uk.ac.manchester.spinnaker.transceiver.TransceiverInterface; -import static uk.ac.manchester.spinnaker.utils.MathUtils.ceildiv; abstract class BoardWorker { /** The transceiver for talking to the SpiNNaker machine. */ @@ -133,7 +132,7 @@ protected void mallocCore(CoreLocation xyp) throws txrx.writeUser0(xyp, start); storage.setStartAddress(xyp, start); - int nextPointer = start.address + APP_PTR_TABLE_BYTE_SIZE; + int nextPointer = start.address() + APP_PTR_TABLE_BYTE_SIZE; for (var regionNum : regionSizes.keySet()) { var size = regionSizes.get(regionNum); storage.setRegionPointer(xyp, regionNum, nextPointer); @@ -159,8 +158,7 @@ protected void mallocCore(CoreLocation xyp) throws */ protected void loadCore(CoreLocation xyp) throws IOException, ProcessException, StorageException, InterruptedException { - var pointerTable = - allocate(APP_PTR_TABLE_BYTE_SIZE).order(LITTLE_ENDIAN); + var pointerTable = alloc(APP_PTR_TABLE_BYTE_SIZE); //header pointerTable.putInt(APPDATA_MAGIC_NUM); pointerTable.putInt(DSE_VERSION); @@ -169,7 +167,7 @@ protected void loadCore(CoreLocation xyp) throws IOException, for (int region = 0; region < MAX_MEM_REGIONS; region++) { if (regionInfos.containsKey(region)) { var regionInfo = regionInfos.get(region); - pointerTable.putInt(regionInfo.pointer.address); + pointerTable.putInt(regionInfo.pointer.address()); if (regionInfo.content != null) { var written = writeRegion( xyp, regionInfo.content, regionInfo.pointer); diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/FastDataInProtocol.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/FastDataInProtocol.java index 0b7b4fca4d..310e449261 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/FastDataInProtocol.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/FastDataInProtocol.java @@ -18,8 +18,6 @@ import static java.lang.Integer.toUnsignedLong; import static java.lang.Math.min; import static java.lang.String.format; -import static java.nio.ByteBuffer.allocate; -import static java.nio.ByteOrder.LITTLE_ENDIAN; import static uk.ac.manchester.spinnaker.front_end.dse.FastDataInCommandID.SEND_DATA_TO_LOCATION; import static uk.ac.manchester.spinnaker.front_end.dse.FastDataInCommandID.SEND_SEQ_DATA; import static uk.ac.manchester.spinnaker.front_end.dse.FastDataInCommandID.SEND_TELL_DATA_IN; @@ -27,7 +25,7 @@ import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE; import static uk.ac.manchester.spinnaker.messages.sdp.SDPHeader.Flag.REPLY_NOT_EXPECTED; import static uk.ac.manchester.spinnaker.messages.sdp.SDPPort.GATHERER_DATA_SPEED_UP; -import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.slice; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.alloc; import static uk.ac.manchester.spinnaker.utils.MathUtils.ceildiv; import java.nio.ByteBuffer; @@ -123,10 +121,10 @@ private SDPHeader header() { */ SDPMessage dataToLocation(MemoryLocation baseAddress, int numPackets, int transactionId) { - var payload = allocate(BYTES_FOR_LOCATION_PACKET).order(LITTLE_ENDIAN); + var payload = alloc(BYTES_FOR_LOCATION_PACKET); payload.putInt(SEND_DATA_TO_LOCATION.value); payload.putInt(transactionId); - payload.putInt(baseAddress.address); + payload.putInt(baseAddress.address()); payload.putShort((short) boardLocalDestination.getY()); payload.putShort((short) boardLocalDestination.getX()); payload.putInt(numPackets - 1); @@ -147,7 +145,7 @@ SDPMessage dataToLocation(MemoryLocation baseAddress, int numPackets, * If the sequence number is nonsense. */ SDPMessage seqData(ByteBuffer data, int seqNum, int transactionId) { - var payload = allocate(BYTES_PER_FULL_PACKET).order(LITTLE_ENDIAN); + var payload = alloc(BYTES_PER_FULL_PACKET); int position = calculatePositionFromSequenceNumber(seqNum); if (position >= data.limit()) { throw new RuntimeException(format( @@ -164,8 +162,9 @@ SDPMessage seqData(ByteBuffer data, int seqNum, int transactionId) { } private int putBuffer(ByteBuffer data, int position, ByteBuffer payload) { - var slice = slice(data, position, - min(data.remaining() - position, payload.remaining())); + var slice = data + .slice(position, + min(data.remaining() - position, payload.remaining())); payload.put(slice).flip(); return slice.position(); } @@ -182,7 +181,7 @@ private int calculatePositionFromSequenceNumber(int seqNum) { * @return The message indicating the end of the data. */ SDPMessage tellDataIn(int transactionId) { - var payload = allocate(BYTES_FOR_TELL_PACKET).order(LITTLE_ENDIAN); + var payload = alloc(BYTES_FOR_TELL_PACKET); payload.putInt(SEND_TELL_DATA_IN.value); payload.putInt(transactionId); payload.flip(); diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/FastExecuteDataSpecification.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/FastExecuteDataSpecification.java index cb167c3377..c56991322a 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/FastExecuteDataSpecification.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/FastExecuteDataSpecification.java @@ -15,7 +15,6 @@ */ package uk.ac.manchester.spinnaker.front_end.dse; -import static difflib.DiffUtils.diff; import static java.lang.Integer.toUnsignedLong; import static java.lang.String.format; import static java.lang.System.getProperty; @@ -25,9 +24,9 @@ import static java.util.stream.Collectors.toList; import static java.util.stream.IntStream.range; import static org.slf4j.LoggerFactory.getLogger; +import static uk.ac.manchester.spinnaker.front_end.DebuggingUtils.compareBuffers; import static uk.ac.manchester.spinnaker.front_end.dse.FastDataInProtocol.computeNumPackets; import static uk.ac.manchester.spinnaker.messages.Constants.NBBY; -import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.sliceUp; import static uk.ac.manchester.spinnaker.utils.UnitConstants.MEGABYTE; import static uk.ac.manchester.spinnaker.utils.UnitConstants.NSEC_PER_SEC; @@ -52,10 +51,6 @@ import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.MustBeClosed; -import difflib.ChangeDelta; -import difflib.Chunk; -import difflib.DeleteDelta; -import difflib.InsertDelta; import uk.ac.manchester.spinnaker.front_end.NoDropPacketContext; import uk.ac.manchester.spinnaker.front_end.download.request.Gather; import uk.ac.manchester.spinnaker.front_end.download.request.Monitor; @@ -71,7 +66,6 @@ import uk.ac.manchester.spinnaker.storage.StorageException; import uk.ac.manchester.spinnaker.transceiver.ProcessException; import uk.ac.manchester.spinnaker.transceiver.TransceiverInterface; -import uk.ac.manchester.spinnaker.utils.MathUtils; /** * Implementation of the Data Specification Executor that uses the Fast Data In @@ -228,8 +222,7 @@ private void loadBoard(Ethernet board, DSEStorage storage) } log.info("loading data onto {} cores on board", cores.size()); var gather = gathererForChip.get(board.location); - try (var worker = new FastBoardWorker( - txrx, board, storage, gather)) { + try (var worker = new FastBoardWorker(txrx, board, storage, gather)) { for (var xyp : cores) { worker.mallocCore(xyp); } @@ -307,50 +300,6 @@ baseAddress, toUnsignedLong(size), timeTaken, mbs, } } - private static void compareBuffers(ByteBuffer original, - ByteBuffer downloaded) { - for (int i = 0; i < original.remaining(); i++) { - if (original.get(i) != downloaded.get(i)) { - log.error("downloaded buffer contents different"); - for (var delta : diff(list(original), list(downloaded)) - .getDeltas()) { - if (delta instanceof ChangeDelta) { - var delete = delta.getOriginal(); - var insert = delta.getRevised(); - log.warn( - "swapped {} bytes (SCP) for {} (gather) " - + "at {}->{}", - delete.getLines().size(), - insert.getLines().size(), delete.getPosition(), - insert.getPosition()); - log.info("change {} -> {}", describeChunk(delete), - describeChunk(insert)); - } else if (delta instanceof DeleteDelta) { - var delete = delta.getOriginal(); - log.warn("gather deleted {} bytes at {}", - delete.getLines().size(), delete.getPosition()); - log.info("delete {}", describeChunk(delete)); - } else if (delta instanceof InsertDelta) { - var insert = delta.getRevised(); - log.warn("gather inserted {} bytes at {}", - insert.getLines().size(), insert.getPosition()); - log.info("insert {}", describeChunk(insert)); - } - } - break; - } - } - } - - private static List list(ByteBuffer buffer) { - return sliceUp(buffer, 1).map(ByteBuffer::get).toList(); - } - - private static List describeChunk(Chunk chunk) { - return chunk.getLines().stream().map(MathUtils::hexbyte) - .collect(toList()); - } - /** * The worker class that handles a particular board of a SpiNNaker machine. * Instances of this class are only ever used from a single thread. @@ -359,11 +308,11 @@ private static List describeChunk(Chunk chunk) { * @author Alan Stokes */ private class FastBoardWorker extends BoardWorker implements AutoCloseable { - private ThrottledConnection connection; + private final ThrottledConnection connection; private MissingRecorder missingSequenceNumbers; - private BoardLocal logContext; + private final BoardLocal logContext; private Gather gather; @@ -394,7 +343,7 @@ public void close() throws IOException, ProcessException, * @author Donal Fellows */ @SuppressWarnings("serial") - private class MissingRecorder extends ArrayDeque + private final class MissingRecorder extends ArrayDeque implements AutoCloseable { MissingRecorder() { missingSequenceNumbers = this; @@ -476,7 +425,7 @@ protected int writeRegion(HasCoreLocation core, ByteBuffer content, if (SPINNAKER_COMPARE_UPLOAD != null) { var readBack = txrx.readMemory( core, baseAddress, content.remaining()); - compareBuffers(content, readBack); + compareBuffers(content, readBack, log); } return written; } @@ -730,7 +679,7 @@ private void retransmitMissingPackets(GathererProtocol protocol, * * @author Donal Fellows */ - private class GathererProtocol extends FastDataInProtocol { + private final class GathererProtocol extends FastDataInProtocol { private GathererProtocol(ChipLocation chip, boolean ignored) { super(machine, gathererForChip.get(chip), monitorForChip.get(chip)); } @@ -769,8 +718,10 @@ static class BadDataInMessageException extends RuntimeException { BadDataInMessageException(int code, IntBuffer message) { super("unexpected response code: " + toUnsignedLong(code)); - log.warn("bad message payload: {}", range(0, message.limit()) - .map(i -> message.get(i)).boxed().collect(toList())); + log.warn("bad message payload: {}", + range(0, message.limit()).map(message::get) + .mapToObj(Integer::toUnsignedString) + .collect(toList())); } } @@ -786,8 +737,10 @@ static class CrazySequenceNumberException extends RuntimeException { CrazySequenceNumberException(int remaining, IntBuffer message) { super("crazy number of missing packets: " + toUnsignedLong(remaining)); - log.warn("bad message payload: {}", range(0, message.limit()) - .map(i -> message.get(i)).boxed().collect(toList())); + log.warn("bad message payload: {}", + range(0, message.limit()).map(message::get) + .mapToObj(Integer::toUnsignedString) + .collect(toList())); } } } diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/SystemRouterTableContext.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/SystemRouterTableContext.java index 8423b9ca1e..d715551151 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/SystemRouterTableContext.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/SystemRouterTableContext.java @@ -36,7 +36,7 @@ * * @author Donal Fellows */ -public class SystemRouterTableContext implements AutoCloseable { +public final class SystemRouterTableContext implements AutoCloseable { private static final Logger log = getLogger(SystemRouterTableContext.class); private final CoreSubsets monitorCores; diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/ThrottledConnection.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/ThrottledConnection.java index 0589976724..f315d552f4 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/ThrottledConnection.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/dse/ThrottledConnection.java @@ -50,7 +50,7 @@ * * @author Donal Fellows */ -class ThrottledConnection implements Closeable { +final class ThrottledConnection implements Closeable { private static final Logger log = getLogger(ThrottledConnection.class); /** The minimum interval between messages, in nanoseconds. */ diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/iobuf/IobufRequest.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/iobuf/IobufRequest.java index 79cece1c1d..92cbdb06d7 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/iobuf/IobufRequest.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/iobuf/IobufRequest.java @@ -25,15 +25,14 @@ import java.util.List; import java.util.Map; -import javax.validation.Valid; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotNull; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.CoreSubsets; /** diff --git a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/iobuf/Replacer.java b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/iobuf/Replacer.java index deaea31573..7f6feb8ee0 100644 --- a/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/iobuf/Replacer.java +++ b/SpiNNaker-front-end/src/main/java/uk/ac/manchester/spinnaker/front_end/iobuf/Replacer.java @@ -39,7 +39,7 @@ * * @author Donal Fellows */ -class Replacer { +final class Replacer { private static final Logger log = getLogger(Replacer.class); private static final Pattern FORMAT_SEQUENCE = @@ -149,14 +149,9 @@ private void applyReplacements(StringBuilder buffer, String[] parts) { } } - private static final class Replacement { - private final String match; - - private final int index; - + private record Replacement(String match, int index) { private Replacement(MatchResult m, int index) { - match = m.group(); - this.index = index; + this(m.group(), index); } void replace(StringBuilder buffer, String[] parts) { diff --git a/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/download/TestPlacements.java b/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/download/TestPlacements.java index 4de92b29fe..f5a38a5ce5 100644 --- a/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/download/TestPlacements.java +++ b/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/download/TestPlacements.java @@ -23,6 +23,7 @@ import uk.ac.manchester.spinnaker.front_end.download.request.Placement; import uk.ac.manchester.spinnaker.front_end.download.request.Vertex; +import uk.ac.manchester.spinnaker.machine.MemoryLocation; /** * @@ -34,7 +35,7 @@ public class TestPlacements { public void testVertexJson() throws IOException { var url = TestPlacements.class.getResource("/vertex.json"); var fromJson = createMapper().readValue(url, Vertex.class); - assertEquals(1612972372, fromJson.getBaseAddress()); + assertEquals(new MemoryLocation(1612972372), fromJson.base()); } @Test diff --git a/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/iobuf/TestIobufRequest.java b/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/iobuf/TestIobufRequest.java index c326966499..c1f8ac52be 100644 --- a/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/iobuf/TestIobufRequest.java +++ b/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/iobuf/TestIobufRequest.java @@ -32,11 +32,13 @@ public class TestIobufRequest { @Test public void testCorrect() throws IOException { - var input = "{\n" - + " \"/some/path/abc.aplx\": [ [0,0,1], [0,0,2], [0,0,3] ],\n" - + " \"/some/path/def.aplx\": [ [0,1,1], [0,1,2], [0,1,3] ],\n" - + " \"/some/path/ghi.aplx\": [ [1,0,1], [1,0,2], [0,0,4] ]\n" - + "}"; + var input = """ + { + "/some/path/abc.aplx": [ [0,0,1], [0,0,2], [0,0,3] ], + "/some/path/def.aplx": [ [0,1,1], [0,1,2], [0,1,3] ], + "/some/path/ghi.aplx": [ [1,0,1], [1,0,2], [0,0,4] ] + } + """; var req = createMapper().readValue(input, IobufRequest.class); var details = req.getRequestDetails(); @@ -58,11 +60,13 @@ public void testCorrect() throws IOException { @Test public void testIncorrect() { // Overlap between the core subsets; THIS IS BAD and shouldn't happen - var input = "{\n" - + " \"/some/path/abc.aplx\": [ [0,0,1], [0,0,2], [0,0,3] ],\n" - + " \"/some/path/def.aplx\": [ [0,1,1], [0,1,2], [0,0,3] ],\n" - + " \"/some/path/ghi.aplx\": [ [0,0,1], [1,0,2], [0,0,4] ]\n" - + "}"; + var input = """ + { + "/some/path/abc.aplx": [ [0,0,1], [0,0,2], [0,0,3] ], + "/some/path/def.aplx": [ [0,1,1], [0,1,2], [0,0,3] ], + "/some/path/ghi.aplx": [ [0,0,1], [1,0,2], [0,0,4] ] + } + """; var e = assertThrows(ValueInstantiationException.class, () -> createMapper().readValue(input, IobufRequest.class)); diff --git a/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/iobuf/TestReplacer.java b/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/iobuf/TestReplacer.java index 281a209ec5..be6b6c8d82 100644 --- a/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/iobuf/TestReplacer.java +++ b/SpiNNaker-front-end/src/test/java/uk/ac/manchester/spinnaker/front_end/iobuf/TestReplacer.java @@ -33,6 +33,8 @@ public void replace() { replacer.replace("1\u001e2\u001e3\u001e4")); assertEquals("abc,d,1,e,3,f,4", replacer.replace("2\u001e1\u001e3\u001e4")); + assertEquals("abc,d,12345,e,34567,f,45678", + replacer.replace("2\u001e12345\u001e34567\u001e45678")); assertEquals("1,2,3,4", replacer.replace("1,2,3,4")); } } diff --git a/SpiNNaker-machine/pom.xml b/SpiNNaker-machine/pom.xml index f8d2f797e4..2be083a149 100644 --- a/SpiNNaker-machine/pom.xml +++ b/SpiNNaker-machine/pom.xml @@ -39,7 +39,7 @@ limitations under the License. com.fasterxml.jackson.module - jackson-module-jaxb-annotations + jackson-module-jakarta-xmlbind-annotations jakarta.validation diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Chip.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Chip.java index ba1ca4f49d..0bfa2f4b58 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Chip.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Chip.java @@ -29,7 +29,7 @@ import java.util.Objects; import java.util.TreeMap; -import javax.validation.Valid; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.bean.ChipBean; import uk.ac.manchester.spinnaker.machine.tags.TagID; @@ -443,7 +443,7 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - return (obj instanceof Chip) && isNull(difference((Chip) obj)); + return (obj instanceof Chip c) && isNull(difference(c)); } /** diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ChipLocation.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ChipLocation.java index 08d612d67e..7fc7f17f96 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ChipLocation.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ChipLocation.java @@ -15,47 +15,35 @@ */ package uk.ac.manchester.spinnaker.machine; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - import static com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY; import static java.lang.Integer.compare; -import static uk.ac.manchester.spinnaker.machine.MachineDefaults.COORD_SHIFT; import static uk.ac.manchester.spinnaker.machine.MachineDefaults.validateChipLocation; import java.io.Serializable; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.errorprone.annotations.Immutable; /** * The location of a {@link Chip} as an X and Y tuple. - *

- * This class is final as it is used a key in maps. * * @author Alan Stokes * @author Donal Fellows + * @param x + * The X coordinate. + * @param y + * The Y coordinate. */ @Immutable @JsonFormat(shape = ARRAY) -/* - * This is needed in Java 8, but not by Java 10 (which can find the @JsonIgnore - * annotation on the default property all by itself. - */ -@JsonIgnoreProperties({"scamp-core", "scamp_core", "scampCore"}) -public final class ChipLocation +@JsonIgnoreProperties({ "scamp-core", "scamp_core", "scampCore" }) +public record ChipLocation( + @JsonProperty(value = "x", required = true) @ValidX int x, + @JsonProperty(value = "y", required = true) @ValidY int y) implements HasChipLocation, Comparable, Serializable { - private static final long serialVersionUID = -2343484354316378507L; - - /** The X coordinate. */ - @ValidX - private final int x; - - /** The Y coordinate. */ - @ValidY - private final int y; - /** * The location (0,0), which is in the bottom/left corner and typically the * Ethernet-enabled chip for the lead board of an allocation. It is also @@ -71,39 +59,10 @@ public final class ChipLocation */ public static final ChipLocation ONE_ZERO = new ChipLocation(1, 0); - /** - * Create the location of a chip on a SpiNNaker machine. - * - * @param x - * The X coordinate - * @param y - * The Y coordinate - * @throws IllegalArgumentException - * Thrown is either x or y is negative or too big. - */ + /** Create the location of a chip on a SpiNNaker machine. */ @JsonCreator - public ChipLocation(@JsonProperty(value = "x", required = true) int x, - @JsonProperty(value = "y", required = true) int y) { + public ChipLocation { validateChipLocation(x, y); - this.x = x; - this.y = y; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof ChipLocation) { - var that = (ChipLocation) obj; - return (x == that.x) && (y == that.y); - } - return false; - } - - @Override - public int hashCode() { - return (x << COORD_SHIFT) ^ y; } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/CoreLocation.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/CoreLocation.java index cdd6723ebf..5fd9404e82 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/CoreLocation.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/CoreLocation.java @@ -16,8 +16,6 @@ package uk.ac.manchester.spinnaker.machine; import static java.lang.Integer.compare; -import static uk.ac.manchester.spinnaker.machine.MachineDefaults.COORD_SHIFT; -import static uk.ac.manchester.spinnaker.machine.MachineDefaults.CORE_SHIFT; import static uk.ac.manchester.spinnaker.machine.MachineDefaults.validateCoreLocation; import java.io.Serializable; @@ -26,44 +24,22 @@ /** * The location of a {@link Processor} on a {@link Chip} as an X, Y, P tuple. - *

- * This class is final as it is used a key in maps. * * @author Alan Stokes * @author Donal Fellows + * @param x + * The X coordinate, in range 0..255 + * @param y + * The Y coordinate, in range 0..255 + * @param p + * The P coordinate, in range 0..17 */ @Immutable -public final class CoreLocation +public record CoreLocation(@ValidX int x, @ValidY int y, @ValidP int p) implements HasCoreLocation, Comparable, Serializable { - private static final long serialVersionUID = 2930811082362121057L; - - /** The X coordinate. */ - @ValidX - private final int x; - - /** The Y coordinate. */ - @ValidY - private final int y; - - /** The P coordinate. */ - @ValidP - private final int p; - - /** - * Create the location of a core on a SpiNNaker machine. - * - * @param x - * The X coordinate, in range 0..255 - * @param y - * The Y coordinate, in range 0..255 - * @param p - * The P coordinate, in range 0..17 - */ - public CoreLocation(int x, int y, int p) { + /** Create an instance. */ + public CoreLocation { validateCoreLocation(x, y, p); - this.x = x; - this.y = y; - this.p = p; } /** @@ -78,23 +54,6 @@ public CoreLocation(HasChipLocation chip, int p) { this(chip.getX(), chip.getY(), p); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof CoreLocation) { - var that = (CoreLocation) obj; - return (x == that.x) && (y == that.y) && (p == that.p); - } - return false; - } - - @Override - public int hashCode() { - return (((x << COORD_SHIFT) ^ y) << CORE_SHIFT) ^ p; - } - @Override public int getX() { return x; diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/CoreSubsets.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/CoreSubsets.java index c5ac94f64e..a1ef4f4c50 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/CoreSubsets.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/CoreSubsets.java @@ -25,8 +25,7 @@ import java.util.Set; import java.util.TreeMap; -import javax.validation.Valid; - +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.utils.DoubleMapIterable; import uk.ac.manchester.spinnaker.utils.MappableIterable; @@ -293,11 +292,8 @@ public int hashCode() { */ @Override public boolean equals(Object obj) { - if (obj instanceof CoreSubsets) { - var other = (CoreSubsets) obj; - return Objects.equals(locations, other.locations); - } - return false; + return (obj instanceof CoreSubsets other) + && Objects.equals(locations, other.locations); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Direction.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Direction.java index 0f1ebe9393..9d3a01d74e 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Direction.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Direction.java @@ -77,20 +77,14 @@ public enum Direction { * @return The inverse direction. */ Direction inverse() { - switch (this) { - case EAST: - return WEST; - case NORTHEAST: - return SOUTHWEST; - case NORTH: - return SOUTH; - case WEST: - return EAST; - case SOUTHWEST: - return NORTHEAST; - default: - return NORTH; - } + return switch (this) { + case EAST -> WEST; + case NORTHEAST -> SOUTHWEST; + case NORTH -> SOUTH; + case WEST -> EAST; + case SOUTHWEST -> NORTHEAST; + default -> NORTH; + }; } /** diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Link.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Link.java index 1f9c01d76a..d8c1bee923 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Link.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Link.java @@ -15,17 +15,11 @@ */ package uk.ac.manchester.spinnaker.machine; -import static java.util.Objects.hash; -import static org.slf4j.LoggerFactory.getLogger; - -import java.util.Objects; - -import javax.validation.Valid; - -import org.slf4j.Logger; - import com.google.errorprone.annotations.Immutable; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; + /** * Represents a directional link between SpiNNaker chips in the machine. * @@ -33,50 +27,19 @@ * "https://github.com/SpiNNakerManchester/SpiNNMachine/blob/master/spinn_machine/link.py"> * Python Version * @author Christian-B + * @param source + * The coordinates of the source chip of the link. + * @param sourceLinkDirection + * The direction of the link in the source chip. + * @param destination + * The coordinate of the destination chip of the link. */ @Immutable -public final class Link { - private static final Logger log = getLogger(Link.class); - - /** The coordinates of the source chip of the link. */ - @Valid - public final ChipLocation source; - - /** The ID/Direction of the link in the source chip. */ - public final Direction sourceLinkDirection; - - /** The coordinate of the destination chip of the link. */ - @Valid - public final ChipLocation destination; - +public record Link(@Valid ChipLocation source, + @NotNull Direction sourceLinkDirection, + @Valid ChipLocation destination) { // Note: multicast_default_from and multicast_default_to not implemented - - /** - * Main Constructor which sets all parameters. - *

- * Specifically there is no check that the destination is the - * typical one for this source and direction pair. - * - * @param source - * The coordinates of the source chip of the link. - * @param destination - * The coordinate of the destination chip of the link. - * @param sourceLinkDirection - * The Direction of the link in the source chip. - */ - public Link(ChipLocation source, Direction sourceLinkDirection, - ChipLocation destination) { - this.source = source; - this.sourceLinkDirection = sourceLinkDirection; - this.destination = destination; - } - /** - * Main Constructor which sets all parameters. - *

- * Specifically there is no check that the destination is the - * typical one for this source and direction pair. - * * @param source * The coordinates of the source chip of the link. * @param destination @@ -90,11 +53,6 @@ public Link(HasChipLocation source, Direction sourceLinkDirection, destination.asChipLocation()); } - @Override - public int hashCode() { - return hash(source, sourceLinkDirection, destination); - } - /** * @param source * The coordinates of the source chip of the link. @@ -108,22 +66,6 @@ public Link(HasChipLocation source, int sourceLinkId, this(source, Direction.byId(sourceLinkId), destination); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } else if (!(obj instanceof Link)) { - return false; - } - var other = (Link) obj; - log.trace("Equals called {} {}", this, other); - if (sourceLinkDirection != other.sourceLinkDirection) { - return false; - } - return Objects.equals(source, other.source) - && Objects.equals(destination, other.destination); - } - @Override public String toString() { return "Link{" + "source=" + source + ", source_link_id=" diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Machine.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Machine.java index 7af24824cd..4d6fda9915 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Machine.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Machine.java @@ -41,7 +41,7 @@ import java.util.SortedMap; import java.util.TreeMap; -import javax.validation.Valid; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.bean.MachineBean; import uk.ac.manchester.spinnaker.machine.datalinks.FPGALinkData; @@ -145,8 +145,8 @@ public Machine(MachineDimensions machineDimensions, Iterable chips, * Descriptor holding the values to set. */ public Machine(MachineBean bean) { - this(bean.getMachineDimensions(), bean.getRoot()); - for (var chipBean : bean.getChips()) { + this(bean.dimensions(), bean.root()); + for (var chipBean : bean.chips()) { chipBean.addDefaults(bean); addChip(new Chip(chipBean, this)); } @@ -226,9 +226,9 @@ public Machine rebuild(Set ignoreChips, var downDirections = ignoreLinks.get(location); var links = new ArrayList(); for (var link : chip.router) { - if (downDirections.contains(link.sourceLinkDirection)) { + if (downDirections.contains(link.sourceLinkDirection())) { log.info("Rebuilt machine without Link {} {}", - location, link.sourceLinkDirection); + location, link.sourceLinkDirection()); } else { links.add(link); } @@ -258,15 +258,15 @@ public final void addChip(Chip chip) { "There is already a Chip at location: " + location); } - if (chip.getX() >= machineDimensions.width) { + if (chip.getX() >= machineDimensions.width()) { throw new IllegalArgumentException("Chip x: " + chip.getX() + " is too high for a machine with width " - + machineDimensions.width); + + machineDimensions.width()); } - if (chip.getY() >= machineDimensions.height) { + if (chip.getY() >= machineDimensions.height()) { throw new IllegalArgumentException("Chip y: " + chip.getY() + " is too high for a machine with height " - + machineDimensions.height + " " + chip); + + machineDimensions.height() + " " + chip); } chips.put(location, chip); @@ -508,7 +508,7 @@ public final Chip getChipOverLink(HasChipLocation source, * @return The maximum possible X-coordinate. */ public final int maxChipX() { - return machineDimensions.width - 1; + return machineDimensions.width() - 1; } /** @@ -520,7 +520,7 @@ public final int maxChipX() { * @return The maximum possible Y-coordinate. */ public final int maxChipY() { - return machineDimensions.height - 1; + return machineDimensions.height() - 1; } /** @@ -632,13 +632,13 @@ public final void addSpinnakerLinks() { */ public final ChipLocation normalizedLocation(int x, int y) { if (version.horizontalWrap) { - x = (x + machineDimensions.width) % machineDimensions.width; - } else if (x < 0 || x >= machineDimensions.width) { + x = (x + machineDimensions.width()) % machineDimensions.width(); + } else if (x < 0 || x >= machineDimensions.width()) { return null; } if (version.verticalWrap) { - y = (y + machineDimensions.height) % machineDimensions.height; - } else if (y < 0 || y >= machineDimensions.height) { + y = (y + machineDimensions.height()) % machineDimensions.height(); + } else if (y < 0 || y >= machineDimensions.height()) { return null; } if (x < 0 || y < 0) { @@ -905,15 +905,15 @@ public Map> findAbnormalLinks() { * rules, it's considered to be normal. Everything else is * abnormal. */ - if (hasChipAt(link.destination) - && getChipAt(link.destination).router - .hasLink(link.sourceLinkDirection.inverse())) { + if (hasChipAt(link.destination()) + && getChipAt(link.destination()).router.hasLink( + link.sourceLinkDirection().inverse())) { continue; } abnormalLinks - .computeIfAbsent(link.source, + .computeIfAbsent(link.source(), __ -> noneOf(Direction.class)) - .add(link.sourceLinkDirection); + .add(link.sourceLinkDirection()); } } return unmodifiableMap(abnormalLinks); @@ -945,7 +945,7 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - return (obj instanceof Machine) && isNull(difference((Machine) obj)); + return (obj instanceof Machine m) && isNull(difference(m)); } /** diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MachineDimensions.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MachineDimensions.java index daa71b03e3..f6baf65685 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MachineDimensions.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MachineDimensions.java @@ -17,44 +17,18 @@ import com.google.errorprone.annotations.Immutable; -/** Represents the size of a machine in chips. */ +/** + * Represents the size of a machine in chips. + * + * @param width + * The width of the machine, in chips. + * @param height + * The height of the machine, in chips. + */ @Immutable -public final class MachineDimensions { - /** The width of the machine in chips. */ - @ValidMachineWidth - public final int width; - - /** The height of the machine in chips. */ - @ValidMachineHeight - public final int height; - - /** - * Create a new instance. - * - * @param width - * The width of the machine, in chips. - * @param height - * The height of the machine, in chips. - */ - public MachineDimensions(int width, int height) { - this.width = width; - this.height = height; - } - - @Override - public boolean equals(Object o) { - if (o instanceof MachineDimensions) { - var dim = (MachineDimensions) o; - return (width == dim.width) && (height == dim.height); - } - return false; - } - - @Override - public int hashCode() { - return width << 16 | height; - } - +public record MachineDimensions(// + @ValidMachineWidth int width, // + @ValidMachineHeight int height) { @Override public String toString() { return "Width:" + width + " Height:" + height; diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MachineVersion.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MachineVersion.java index 548b92e5b6..8c0d1debba 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MachineVersion.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MachineVersion.java @@ -279,17 +279,17 @@ public static MachineVersion bySize(MachineDimensions dimensions) { return possible; } } - if ((dimensions.width % TRIAD_HEIGHT == 0) - && (dimensions.height % TRIAD_WIDTH == 0)) { + if ((dimensions.width() % TRIAD_HEIGHT == 0) + && (dimensions.height() % TRIAD_WIDTH == 0)) { return TRIAD_WITH_WRAPAROUND; } - if (((dimensions.width - HALF_SIZE) % TRIAD_HEIGHT == 0) - && ((dimensions.height - HALF_SIZE) % TRIAD_WIDTH == 0)) { + if (((dimensions.width() - HALF_SIZE) % TRIAD_HEIGHT == 0) + && ((dimensions.height() - HALF_SIZE) % TRIAD_WIDTH == 0)) { return TRIAD_NO_WRAPAROUND; } // Handle 4 chip board extended with virtual_chips - if (dimensions.width < SIZE_X_OF_ONE_BOARD) { - if (dimensions.height < SIZE_Y_OF_ONE_BOARD) { + if (dimensions.width() < SIZE_X_OF_ONE_BOARD) { + if (dimensions.height() < SIZE_Y_OF_ONE_BOARD) { return EXTENDED_SMALL; } } @@ -298,22 +298,22 @@ public static MachineVersion bySize(MachineDimensions dimensions) { * three board toroid we need at least two board on top of each other or * next to each other */ - if (dimensions.width < SIZE_X_OF_ONE_BOARD * 2) { - if (dimensions.height < SIZE_Y_OF_ONE_BOARD * 2) { + if (dimensions.width() < SIZE_X_OF_ONE_BOARD * 2) { + if (dimensions.height() < SIZE_Y_OF_ONE_BOARD * 2) { throw new IllegalArgumentException( "Dimensions " + dimensions + "too small!"); } } - if ((dimensions.width % TRIAD_HEIGHT == 0) - && ((dimensions.height - HALF_SIZE) % TRIAD_WIDTH == 0)) { + if ((dimensions.width() % TRIAD_HEIGHT == 0) + && ((dimensions.height() - HALF_SIZE) % TRIAD_WIDTH == 0)) { return TRIAD_WITH_HORIZONTAL_WRAP; } - if (((dimensions.width - HALF_SIZE) % TRIAD_HEIGHT == 0) - && (dimensions.height % TRIAD_WIDTH == 0)) { + if (((dimensions.width() - HALF_SIZE) % TRIAD_HEIGHT == 0) + && (dimensions.height() % TRIAD_WIDTH == 0)) { return TRIAD_WITH_VERTICAL_WRAP; } - if (dimensions.width % HALF_SIZE == 0 - && dimensions.height % HALF_SIZE == 0) { + if (dimensions.width() % HALF_SIZE == 0 + && dimensions.height() % HALF_SIZE == 0) { return NONE_TRIAD_LARGE; } throw new IllegalArgumentException("Dimensions " + dimensions diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MemoryLocation.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MemoryLocation.java index 2c3ba81c8c..a8a0461962 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MemoryLocation.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MemoryLocation.java @@ -25,32 +25,24 @@ * is in. * * @author Donal Fellows + * @param address + * The actual location. */ @Immutable -public final class MemoryLocation implements Comparable { +public record MemoryLocation(int address) + implements Comparable { /** Number of bytes in a SpiNNaker (ARM) word. */ private static final int WORD_SIZE = 4; /** The zero memory location. Often means "no actual address". */ public static final MemoryLocation NULL = new MemoryLocation(0); - /** The actual location. */ - public final int address; - - /** - * @param address - * The actual location. - */ - public MemoryLocation(int address) { - this.address = address; - } - /** * @param address * The actual location. */ public MemoryLocation(long address) { - this.address = convert(address); + this(convert(address)); } /** @@ -101,11 +93,7 @@ public int hashCode() { @Override public boolean equals(Object other) { - if (!(other instanceof MemoryLocation)) { - return false; - } - var o = (MemoryLocation) other; - return o.address == address; + return (other instanceof MemoryLocation o) && (o.address == address); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MulticastRoutingEntry.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MulticastRoutingEntry.java index 6600fd19d0..8debf8d095 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MulticastRoutingEntry.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/MulticastRoutingEntry.java @@ -19,7 +19,7 @@ /** A multicast packet routing table entry. */ @Immutable -public class MulticastRoutingEntry extends RoutingEntry { +public final class MulticastRoutingEntry extends RoutingEntry { private final int key; private final int mask; diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/NeighbourIterator.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/NeighbourIterator.java index 87b6441846..d81877e87a 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/NeighbourIterator.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/NeighbourIterator.java @@ -48,6 +48,6 @@ public boolean hasNext() { @Override public ChipLocation next() { - return linksIter.next().destination; + return linksIter.next().destination(); } } diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Processor.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Processor.java index 8f70890884..430a4ad26a 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Processor.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Processor.java @@ -123,11 +123,8 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (!(obj instanceof Processor)) { - return false; - } - var other = (Processor) obj; - return (processorId == other.processorId) + return (obj instanceof Processor other) + && (processorId == other.processorId) && (clockSpeed == other.clockSpeed) && (isMonitor == other.isMonitor) && (dtcmAvailable == other.dtcmAvailable); diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/RegionLocation.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/RegionLocation.java index afa94e02b5..15a7be4de6 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/RegionLocation.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/RegionLocation.java @@ -16,40 +16,28 @@ package uk.ac.manchester.spinnaker.machine; import static java.util.Comparator.comparing; -import static uk.ac.manchester.spinnaker.machine.MachineDefaults.COORD_SHIFT; -import static uk.ac.manchester.spinnaker.machine.MachineDefaults.CORE_SHIFT; -import static uk.ac.manchester.spinnaker.machine.MachineDefaults.REGION_SHIFT; import java.util.Comparator; import com.google.errorprone.annotations.Immutable; /** - * Holding case for a CoreLocation (X, Y and P) and the recording region ID. + * Container for a CoreLocation (X, Y and P) and the recording region ID. * * @author Christian-B + * @param x + * The Chip / Core's X value. + * @param y + * The Chip / Core's Y value. + * @param p + * The Core's P value. + * @param region + * The recording region ID. */ @Immutable -public final class RegionLocation +public record RegionLocation(// + @ValidX int x, @ValidY int y, @ValidP int p, int region) implements HasCoreLocation, Comparable { - /** The Chip / Core's X value. */ - @ValidX - public final int x; - - /** The Chip / Core's Y value. */ - @ValidY - public final int y; - - /** The Core's P value. */ - @ValidP - public final int p; - - /** The recording region ID. */ - public final int region; - - /** Precalculated hashcode. */ - private final int hashcode; - /** * Creates the region based on a core and a region. * @@ -59,12 +47,7 @@ public final class RegionLocation * The ID of the region to use. */ public RegionLocation(HasCoreLocation core, int region) { - x = core.getX(); - y = core.getY(); - p = core.getP(); - this.region = region; - hashcode = ((((x << COORD_SHIFT) ^ y) << CORE_SHIFT) - ^ p) << REGION_SHIFT ^ region; + this(core.getX(), core.getY(), core.getP(), region); } @Override @@ -84,36 +67,18 @@ public int getY() { /** Comparator for region locations. */ public static final Comparator COMPARATOR = - comparing(RegionLocation::getX) // - .thenComparing(RegionLocation::getY) - .thenComparing(RegionLocation::getP) - .thenComparing(rl -> rl.region); + comparing(RegionLocation::x) // + .thenComparing(RegionLocation::y) + .thenComparing(RegionLocation::p) + .thenComparing(RegionLocation::region); @Override public int compareTo(RegionLocation o) { return COMPARATOR.compare(this, o); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof RegionLocation)) { - return false; - } - var that = (RegionLocation) obj; - return (x == that.x) && (y == that.y) && (p == that.p) - && (region == that.region); - } - @Override public String toString() { return "X:" + x + " Y:" + y + " P:" + p + "R: " + region; } - - @Override - public int hashCode() { - return hashcode; - } } diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Router.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Router.java index b6f7b83b6f..495a5a7062 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Router.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/Router.java @@ -18,7 +18,6 @@ import static java.util.Collections.unmodifiableCollection; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toUnmodifiableList; import static uk.ac.manchester.spinnaker.machine.MachineDefaults.ROUTER_AVAILABLE_ENTRIES; import java.util.Collection; @@ -185,10 +184,10 @@ public Router(HasChipLocation source, int nAvailableMulticastEntries, * has already been added. */ public void addLink(Link link) throws IllegalArgumentException { - if (links.containsKey(link.sourceLinkDirection)) { + if (links.containsKey(link.sourceLinkDirection())) { throw new IllegalArgumentException("Link already exists: " + link); } - links.put(link.sourceLinkDirection, link); + links.put(link.sourceLinkDirection(), link); } /** @@ -248,7 +247,7 @@ public int size() { * @return A Stream over the destination locations. */ public Stream streamNeighbouringChipsCoords() { - return links.values().stream().map(link -> link.destination); + return links.values().stream().map(Link::destination); } /** @@ -271,14 +270,13 @@ public MappableIterable iterNeighbouringChipsCoords() { * @return The destination locations */ public List neighbouringChipsCoords() { - return links.values().stream().map(link -> link.destination) - .collect(toUnmodifiableList()); + return links.values().stream().map(Link::destination).toList(); } @Override public String toString() { return links.entrySet().stream().map( - entry -> entry.getKey() + ":" + entry.getValue().destination) + entry -> entry.getKey() + ":" + entry.getValue().destination()) .collect(joining(" ", "Router[", "]")); } diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/RoutingEntry.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/RoutingEntry.java index c574fe5582..d4fc68cc76 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/RoutingEntry.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/RoutingEntry.java @@ -25,14 +25,14 @@ import java.util.LinkedHashSet; import java.util.Set; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotNull; import com.google.errorprone.annotations.Immutable; /** A basic SpiNNaker routing entry. */ @Immutable @SuppressWarnings("Immutable") // Error Prone can't figure out this is true -public class RoutingEntry { +public sealed class RoutingEntry permits MulticastRoutingEntry { private final Set<@ValidP @NotNull Integer> processorIDs; private final Set linkIDs; diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/SpiNNakerTriadGeometry.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/SpiNNakerTriadGeometry.java index 1ec946199f..1833a9bde3 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/SpiNNakerTriadGeometry.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/SpiNNakerTriadGeometry.java @@ -249,13 +249,13 @@ public Set getPotentialRootChips( var results = new LinkedHashSet(); int maxWidth; int maxHeight; - if (dimensions.width % triadWidth == 0 - && dimensions.height % triadHeight == 0) { - maxWidth = dimensions.width; - maxHeight = dimensions.height; + if (dimensions.width() % triadWidth == 0 + && dimensions.height() % triadHeight == 0) { + maxWidth = dimensions.width(); + maxHeight = dimensions.height(); } else { - maxWidth = dimensions.width - SIZE_X_OF_ONE_BOARD + 1; - maxHeight = dimensions.height - SIZE_Y_OF_ONE_BOARD + 1; + maxWidth = dimensions.width() - SIZE_X_OF_ONE_BOARD + 1; + maxHeight = dimensions.height() - SIZE_Y_OF_ONE_BOARD + 1; if (maxWidth < 0 || maxHeight < 0) { results.add(ZERO_ZERO); return results; diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidMachineHeight.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidMachineHeight.java index 355cb8a8d5..985c679da6 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidMachineHeight.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidMachineHeight.java @@ -26,11 +26,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.Positive; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Positive; /** * Validates that a machine's height (in chips) is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidMachineWidth.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidMachineWidth.java index 7531d58038..11e8ef6a8c 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidMachineWidth.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidMachineWidth.java @@ -26,11 +26,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.Positive; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Positive; /** * Validates that a machine's width (in chips) is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidP.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidP.java index ea4011ed15..ed9c7541de 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidP.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidP.java @@ -25,11 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.PositiveOrZero; /** * Validates that a chip processor number is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidX.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidX.java index c4b2d87f6c..b7b0de61eb 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidX.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidX.java @@ -25,11 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.PositiveOrZero; /** * Validates that a chip X coordinate is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidY.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidY.java index 269d4a48df..bd43f92742 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidY.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/ValidY.java @@ -25,11 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.PositiveOrZero; /** * Validates that a chip Y coordinate is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/ChipBean.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/ChipBean.java index 7efe7ca210..f093de09ed 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/ChipBean.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/ChipBean.java @@ -15,14 +15,13 @@ */ package uk.ac.manchester.spinnaker.machine.bean; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonFormat; import static com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY; -import javax.validation.Valid; - +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.Chip; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; @@ -116,9 +115,9 @@ public String toString() { public void addDefaults(MachineBean bean) { ChipResources defaults; if (details.getIpAddress() == null) { - defaults = bean.getStandardResources(); + defaults = bean.standardResources(); } else { - defaults = bean.getEthernetResources(); + defaults = bean.ethernetResources(); } if (resources == null) { resources = defaults; diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/ChipDetails.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/ChipDetails.java index c4d0cc6964..2e7517f139 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/ChipDetails.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/ChipDetails.java @@ -24,14 +24,13 @@ import java.util.List; import java.util.Set; -import javax.validation.Valid; -import javax.validation.constraints.Max; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.PositiveOrZero; - import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.machine.Chip; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.Direction; @@ -162,8 +161,8 @@ public ChipLocation getLinkDestination(Direction direction, HasChipLocation source, Machine machine) { if (links != null) { for (var bean : links) { - if (bean.sourceDirection == direction) { - return bean.destination; + if (bean.sourceDirection() == direction) { + return bean.destination(); } } } diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/LinkBean.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/LinkBean.java index eddeeeffe7..79afd93c23 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/LinkBean.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/LinkBean.java @@ -15,12 +15,11 @@ */ package uk.ac.manchester.spinnaker.machine.bean; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.Chip; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.Direction; @@ -32,17 +31,14 @@ * of the JSON model, the chip coordinates of the source are implicit. * * @author Christian-B + * @param destination + * Where the link is going. + * @param sourceDirection + * What direction the link is going in. */ -@UsedInJavadocOnly({Chip.class, Link.class}) -public class LinkBean { - /** Where the link is going. */ - @Valid - public final ChipLocation destination; - - /** What direction the link is going in. */ - @NotNull - public final Direction sourceDirection; - +@UsedInJavadocOnly({ Chip.class, Link.class }) +public record LinkBean(@Valid ChipLocation destination, + @NotNull Direction sourceDirection) { /** * Make an instance. * @@ -61,7 +57,7 @@ public LinkBean( int destinationX, @JsonProperty(value = "destinationY", required = true) int destinationY) { - destination = new ChipLocation(destinationX, destinationY); - sourceDirection = Direction.byId(sourceLinkId); + this(new ChipLocation(destinationX, destinationY), + Direction.byId(sourceLinkId)); } } diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/MachineBean.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/MachineBean.java index 5067c24429..8ba8cfb5b2 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/MachineBean.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/bean/MachineBean.java @@ -15,12 +15,12 @@ */ package uk.ac.manchester.spinnaker.machine.bean; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; -import javax.validation.Valid; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.Machine; import uk.ac.manchester.spinnaker.machine.MachineDimensions; @@ -30,26 +30,25 @@ * Used to build a {@link Machine} from JSON. * * @author Christian-B + * @param dimensions + * The dimensions of the machine. + * @param root + * The root chip of the machine. + * @param ethernetResources + * The default resources of ethernet-enabled chips. + * @param standardResources + * The default resources of chips that are not ethernet-enabled. + * @param chips + * The chips. */ @UsedInJavadocOnly(Machine.class) -public class MachineBean { - @Valid - private final MachineDimensions dimensions; - - @Valid - private final ChipLocation root; - - @Valid - private final ChipResources ethernetResources; - - @Valid - private final ChipResources standardResources; - - private final List<@Valid ChipBean> chips; - +public record MachineBean(// + @Valid MachineDimensions dimensions, // + @Valid ChipLocation root, // + @Valid ChipResources ethernetResources, // + @Valid ChipResources standardResources, // + List<@Valid ChipBean> chips) { /** - * Make an instance. - * * @param height * The height of the Machine in Chips * @param width @@ -65,6 +64,7 @@ public class MachineBean { * @param chips * Beans for each Chips on the machine. */ + @JsonCreator public MachineBean( @JsonProperty(value = "height", required = true) int height, @JsonProperty(value = "width", required = true) int width, @@ -75,37 +75,8 @@ public MachineBean( ChipResources standardResources, @JsonProperty(value = "chips", required = true) List chips) { - dimensions = new MachineDimensions(width, height); - this.root = root; - this.chips = chips; - this.ethernetResources = ethernetResources; - this.standardResources = standardResources; - } - - /** @return The dimensions of the machine. */ - @JsonIgnore - public MachineDimensions getMachineDimensions() { - return dimensions; - } - - /** @return The root chip of the machine. */ - public ChipLocation getRoot() { - return root; - } - - /** @return the default resources of ethernet-enabled chips */ - public ChipResources getEthernetResources() { - return ethernetResources; - } - - /** @return the default resources of chips that are not ethernet-enabled */ - public ChipResources getStandardResources() { - return standardResources; - } - - /** @return the chips */ - public List getChips() { - return chips; + this(new MachineDimensions(width, height), root, ethernetResources, + standardResources, chips); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPBoard.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPBoard.java index 78fd271e8c..ed2cea109f 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPBoard.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPBoard.java @@ -19,41 +19,28 @@ * Wrapper for a board number so that it can't get mixed up with other integers. * * @author Donal Fellows + * @param board + * The board number. */ -public final class BMPBoard { +public record BMPBoard(@ValidBoardNumber int board) { /** * The maximum board number. There can be only up to 24 boards per frame. */ public static final int MAX_BOARD_NUMBER = 23; - /** The board number. */ - @ValidBoardNumber - public final int board; - /** - * @param board - * The board number. + * Convert to a full C, F, B tuple. + * + * @param coords + * The coordinates of the frame. + * @return The C, F, B tuple. */ - public BMPBoard(int board) { - this.board = board; + public BMPLocation locate(BMPCoords coords) { + return new BMPLocation(coords.cabinet(), coords.frame(), board); } @Override public String toString() { return "board=" + board; } - - @Override - public int hashCode() { - return board; - } - - @Override - public boolean equals(Object o) { - if (o instanceof BMPBoard) { - var b = (BMPBoard) o; - return board == b.board; - } - return false; - } } diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPCoords.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPCoords.java index bda64bc68d..7830dd8a5c 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPCoords.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPCoords.java @@ -26,6 +26,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.google.errorprone.annotations.Immutable; +import uk.ac.manchester.spinnaker.utils.ValueHolder; + /** * A simple description of a BMP to talk to. Supports equality and being used as * a hash key. @@ -47,38 +49,21 @@ *

[c:3,f:2]
* * @author Donal Fellows + * @param cabinet + * The ID of the cabinet that contains the frame that contains the + * BMPs. + * @param frame + * The ID of the frame that contains the master BMP. Frames are + * contained within a cabinet. */ @Immutable @JsonDeserialize(using = BMPCoords.Deserializer.class) -public final class BMPCoords implements Comparable { +public record BMPCoords(@ValidCabinetNumber int cabinet, + @ValidFrameNumber int frame) implements Comparable { /** Parses the result of {@link #toString()}. */ private static final Pattern PATTERN = Pattern.compile("^\\[c:(\\d+),f:(\\d+)\\]$"); - /** The ID of the cabinet that contains the frame that contains the BMPs. */ - @ValidCabinetNumber - public final int cabinet; - - /** - * The ID of the frame that contains the master BMP. Frames are contained - * within a cabinet. - */ - @ValidFrameNumber - public final int frame; - - /** - * Create an instance. - * - * @param cabinet - * Cabinet number. - * @param frame - * Frame number. - */ - public BMPCoords(int cabinet, int frame) { - this.cabinet = cabinet; - this.frame = frame; - } - /** * Create an instance from its serial form. This is the form produced by * {@link #toString()}. The serial form (where the numbers may vary) is: @@ -89,48 +74,20 @@ public BMPCoords(int cabinet, int frame) { * * @param serialForm * The form to deserialise. + * @return The deserialised value. * @throws IllegalArgumentException * If the string is not in the right form. */ @JsonCreator - public BMPCoords(String serialForm) { + public static BMPCoords parse(String serialForm) { var m = PATTERN.matcher(serialForm); if (!m.matches()) { throw new IllegalArgumentException("bad argument: " + serialForm); } int idx = 0; - cabinet = parseInt(m.group(++idx)); - frame = parseInt(m.group(++idx)); - } - - /** - * @return The ID of the cabinet that contains the frame that contains the - * BMPs. - */ - public int getCabinet() { - return cabinet; - } - - /** - * @return The ID of the frame that contains the master BMP. Frames are - * contained within a cabinet. - */ - public int getFrame() { - return frame; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof BMPCoords) { - var other = (BMPCoords) obj; - return cabinet == other.cabinet && frame == other.frame; - } - return false; - } - - @Override - public int hashCode() { - return cabinet * 5 + frame; + int cabinet = parseInt(m.group(++idx)); + int frame = parseInt(m.group(++idx)); + return new BMPCoords(cabinet, frame); } @Override @@ -147,6 +104,17 @@ public int compareTo(BMPCoords other) { return compare(frame, other.frame); } + /** + * Convert to a full C, F, B tuple. + * + * @param board + * The coordinate of the board. + * @return The C, F, B tuple. + */ + public BMPLocation locate(BMPBoard board) { + return new BMPLocation(cabinet, frame, board.board()); + } + /** JSON deserializer for {@link BMPCoords}. */ static final class Deserializer extends DeserializerHelper { private static final long serialVersionUID = 1L; @@ -165,31 +133,25 @@ BMPCoords deserializeArray() throws IOException { @Override BMPCoords deserializeObject() throws IOException { - Integer c = null, f = null; + var c = new ValueHolder(); + var f = new ValueHolder(); String name; while ((name = getNextFieldName()) != null) { switch (name) { - case "cabinet": - case "c": - c = requireSetOnceInt(name, c); - break; - case "frame": - case "f": - f = requireSetOnceInt(name, f); - break; - default: - unknownProperty(name); + case "cabinet", "c" -> requireSetOnceInt(name, c); + case "frame", "f" -> requireSetOnceInt(name, f); + default -> unknownProperty(name); } } - if (c == null || f == null) { - missingProperty("c", c, "f", f); + if (c.isEmpty() || f.isEmpty()) { + missingProperty("c", c.getValue(), "f", f.getValue()); } - return new BMPCoords(c, f); + return new BMPCoords(c.getValue(), f.getValue()); } @Override BMPCoords deserializeString(String string) { - return new BMPCoords(string); + return BMPCoords.parse(string); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPLocation.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPLocation.java index 41bbc70764..151c5ad8c9 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPLocation.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/BMPLocation.java @@ -15,29 +15,27 @@ */ package uk.ac.manchester.spinnaker.machine.board; -import static uk.ac.manchester.spinnaker.machine.board.Limits.MAX_FRAME; -import static uk.ac.manchester.spinnaker.machine.board.Limits.MAX_CABINET; +import com.google.errorprone.annotations.Immutable; import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; /** - * Like a {@linkplain CoreLocation core location}, but for BMPs. Note that board - * numbers are not restricted in range like core numbers. + * Like a {@linkplain CoreLocation core location}, but for BMPs. * + * @param cabinet + * The cabinet number. + * @param frame + * The frame number. + * @param board + * The board number. * @author Donal Fellows */ +@Immutable @UsedInJavadocOnly(CoreLocation.class) -public final class BMPLocation implements HasBMPLocation { - @ValidCabinetNumber - private final int cabinet; - - @ValidFrameNumber - private final int frame; - - @ValidBoardNumber - private final int board; - +public record BMPLocation(@ValidCabinetNumber int cabinet, + @ValidFrameNumber int frame, @ValidBoardNumber int board) + implements HasBMPLocation { /** * Create an instance with cabinet and frame both zero. * @@ -48,22 +46,6 @@ public BMPLocation(int board) { this(0, 0, board); } - /** - * Create an instance. - * - * @param cabinet - * The cabinet number. - * @param frame - * The frame number. - * @param board - * The board number. - */ - public BMPLocation(int cabinet, int frame, int board) { - this.cabinet = cabinet; - this.frame = frame; - this.board = board; - } - @Override public int getCabinet() { return cabinet; @@ -80,17 +62,7 @@ public int getBoard() { } @Override - public boolean equals(Object other) { - if (other instanceof BMPLocation) { - var bmp = (BMPLocation) other; - return bmp.cabinet == cabinet && bmp.frame == frame - && bmp.board == board; - } - return false; - } - - @Override - public int hashCode() { - return (((board * MAX_FRAME) + frame) * MAX_CABINET) + cabinet; + public BMPLocation asBMPLocation() { + return this; } } diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/DeserializerHelper.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/DeserializerHelper.java index a00078e7a7..47ab3d1e9d 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/DeserializerHelper.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/DeserializerHelper.java @@ -24,6 +24,8 @@ import com.google.errorprone.annotations.ForOverride; import com.google.errorprone.annotations.FormatMethod; +import uk.ac.manchester.spinnaker.utils.ValueHolder; + /** * A helper class for JSON deserializers. * @@ -47,17 +49,15 @@ public final T deserialize(JsonParser p, DeserializationContext ctxt) try { CONTEXT.set(ctxt); PARSER.set(p); - switch (p.currentToken()) { - case START_ARRAY: - return deserializeArray(); - case START_OBJECT: - return deserializeObject(); - case VALUE_STRING: - return deserializeString(p.getValueAsString()); - default: + return switch (p.currentToken()) { + case START_ARRAY -> deserializeArray(); + case START_OBJECT -> deserializeObject(); + case VALUE_STRING -> deserializeString(p.getValueAsString()); + default -> { ctxt.handleUnexpectedToken(_valueClass, p); - return null; + yield null; } + }; } finally { CONTEXT.remove(); PARSER.remove(); @@ -170,6 +170,14 @@ int requireSetOnceInt(String name, Integer current) throws IOException { return PARSER.get().nextIntValue(0); } + void requireSetOnceInt(String name, ValueHolder holder) + throws IOException { + if (!holder.isEmpty()) { + inputMismatch("Duplicate property '%s'", name); + } + holder.setValue(PARSER.get().nextIntValue(0)); + } + void missingProperty(String n1, Object v1) throws IOException { if (v1 == null) { inputMismatch("Missing required property '%s'", n1); diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/HasBMPLocation.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/HasBMPLocation.java index 3feb14ea77..a7b4f5a56b 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/HasBMPLocation.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/HasBMPLocation.java @@ -21,31 +21,47 @@ */ public interface HasBMPLocation { /** - * @return The cabinet number of the board. Not actually a processor - * coordinate. + * @return The cabinet number of the board. */ @ValidCabinetNumber int getCabinet(); /** - * @return The frame number of the board. Not actually a processor - * coordinate. + * @return The frame number of the board. */ @ValidFrameNumber int getFrame(); /** - * @return The board number of the board. Not actually a processor ID. + * @return The board number of the board. */ @ValidBoardNumber int getBoard(); /** - * Converts (if required) this to a simple X, Y, P tuple. + * Converts (if required) this to a simple C, F, B tuple. * - * @return A CoreLocation representation of the X, Y, P tuple + * @return A BMP location representation of the C, F, B tuple */ default BMPLocation asBMPLocation() { return new BMPLocation(getCabinet(), getFrame(), getBoard()); } + + /** + * Converts this to a C, F tuple. + * + * @return The C, F tuple. + */ + default BMPCoords getBMPCoords() { + return new BMPCoords(getCabinet(), getFrame()); + } + + /** + * Converts this to a bare board descriptor. + * + * @return The bare board descriptor. + */ + default BMPBoard getBMPBoard() { + return new BMPBoard(getBoard()); + } } diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/PhysicalCoords.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/PhysicalCoords.java index 263c6a6dbe..a8d522283f 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/PhysicalCoords.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/PhysicalCoords.java @@ -28,6 +28,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.google.errorprone.annotations.Immutable; +import uk.ac.manchester.spinnaker.utils.ValueHolder; + /** * Physical board coordinates. The {@code cabinet} and {@code frame} (with * multiple frames per cabinet) describe where a board is located within the @@ -48,40 +50,20 @@ *
[c:3,f:2,b:1]
* * @author Donal Fellows + * @param c + * Cabinet number. + * @param f + * Frame number. + * @param b + * Board number. */ @Immutable @JsonDeserialize(using = PhysicalCoords.Deserializer.class) -public final class PhysicalCoords implements Comparable { - /** Cabinet number. */ - @ValidCabinetNumber - public final int c; - - /** Frame number. */ - @ValidFrameNumber - public final int f; - - /** Board number. */ - @ValidBoardNumber - public final int b; - - /** - * Create an instance. - * - * @param c - * Cabinet number. - * @param f - * Frame number. - * @param b - * Board number. - */ - @JsonCreator - public PhysicalCoords(@JsonProperty("c") int c, @JsonProperty("f") int f, - @JsonProperty("b") int b) { - this.c = c; - this.f = f; - this.b = b; - } - +public record PhysicalCoords(// + @JsonProperty("c") @ValidCabinetNumber int c, + @JsonProperty("f") @ValidFrameNumber int f, + @JsonProperty("b") @ValidBoardNumber int b) + implements Comparable { private static final Pattern PATTERN = Pattern.compile("^\\[c:(\\d+),f:(\\d+),b:(\\d+)\\]$"); @@ -95,33 +77,21 @@ public PhysicalCoords(@JsonProperty("c") int c, @JsonProperty("f") int f, * * @param serialForm * The form to deserialise. + * @return The deserialised value. * @throws IllegalArgumentException * If the string is not in the right form. */ @JsonCreator - public PhysicalCoords(String serialForm) { + public static PhysicalCoords parse(String serialForm) { var m = PATTERN.matcher(serialForm); if (!m.matches()) { throw new IllegalArgumentException("bad argument: " + serialForm); } int idx = 0; - c = parseInt(m.group(++idx)); - f = parseInt(m.group(++idx)); - b = parseInt(m.group(++idx)); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof PhysicalCoords) { - var other = (PhysicalCoords) obj; - return c == other.c && f == other.f && b == other.b; - } - return false; - } - - @Override - public int hashCode() { - return c * 25 + f * 5 + b; + int c = parseInt(m.group(++idx)); + int f = parseInt(m.group(++idx)); + int b = parseInt(m.group(++idx)); + return new PhysicalCoords(c, f, b); } @Override @@ -169,35 +139,28 @@ PhysicalCoords deserializeArray() throws IOException { @Override PhysicalCoords deserializeObject() throws IOException { - Integer c = null, f = null, b = null; + var c = new ValueHolder(); + var f = new ValueHolder(); + var b = new ValueHolder(); String name; while ((name = getNextFieldName()) != null) { switch (name) { - case "cabinet": - case "c": - c = requireSetOnceInt(name, c); - break; - case "frame": - case "f": - f = requireSetOnceInt(name, f); - break; - case "board": - case "b": - b = requireSetOnceInt(name, b); - break; - default: - unknownProperty(name); + case "cabinet", "c" -> requireSetOnceInt(name, c); + case "frame", "f" -> requireSetOnceInt(name, f); + case "board", "b" -> requireSetOnceInt(name, b); + default -> unknownProperty(name); } } - if (c == null || f == null || b == null) { - missingProperty("c", c, "f", f, "b", b); + if (c.isEmpty() || f.isEmpty() || b.isEmpty()) { + missingProperty("c", c.getValue(), "f", f.getValue(), "b", + b.getValue()); } - return new PhysicalCoords(c, f, b); + return new PhysicalCoords(c.getValue(), f.getValue(), b.getValue()); } @Override PhysicalCoords deserializeString(String string) { - return new PhysicalCoords(string); + return PhysicalCoords.parse(string); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/TriadCoords.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/TriadCoords.java index 96df0597de..643d02c687 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/TriadCoords.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/TriadCoords.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import uk.ac.manchester.spinnaker.machine.ChipLocation; +import uk.ac.manchester.spinnaker.utils.ValueHolder; /** * Triad coordinates. @@ -49,9 +50,19 @@ *
[x:3,y:2,z:1]
* * @author Donal Fellows + * @param x + * X coordinate of triad. + * @param y + * Y coordinate of triad. + * @param z + * Z coordinate of triad. */ @JsonDeserialize(using = TriadCoords.Deserializer.class) -public final class TriadCoords implements Comparable { +public record TriadCoords(// + @JsonProperty("x") @ValidTriadX int x, + @JsonProperty("y") @ValidTriadY int y, + @JsonProperty("z") @ValidTriadZ int z) + implements Comparable { /** The width and height of a triad, in chips. */ private static final int TRIAD_CHIP_SIZE = 12; @@ -63,36 +74,6 @@ public final class TriadCoords implements Comparable { private static final Pattern PATTERN = Pattern.compile("^\\[x:(\\d+),y:(\\d+),z:(\\d+)\\]$"); - /** X coordinate of triad. */ - @ValidTriadX - public final int x; - - /** Y coordinate of triad. */ - @ValidTriadY - public final int y; - - /** Z coordinate of triad. */ - @ValidTriadZ - public final int z; - - /** - * Create an instance. - * - * @param x - * X coordinate. - * @param y - * Y coordinate. - * @param z - * Z coordinate. - */ - @JsonCreator - public TriadCoords(@JsonProperty("x") int x, @JsonProperty("y") int y, - @JsonProperty("z") int z) { - this.x = x; - this.y = y; - this.z = z; - } - /** * Create an instance from its serial form. The serial form (where the * numbers may vary) is: @@ -103,20 +84,22 @@ public TriadCoords(@JsonProperty("x") int x, @JsonProperty("y") int y, * * @param serialForm * The form to deserialise. + * @return The deserialised value. * @throws IllegalArgumentException * If the string is not in the right form. */ @JsonCreator - public TriadCoords(String serialForm) { + public static TriadCoords parse(String serialForm) { var m = PATTERN.matcher(serialForm); if (!m.matches()) { throw new IllegalArgumentException( "bad argument: " + serialForm); } int idx = 0; - x = parseInt(m.group(++idx)); - y = parseInt(m.group(++idx)); - z = parseInt(m.group(++idx)); + int x = parseInt(m.group(++idx)); + int y = parseInt(m.group(++idx)); + int z = parseInt(m.group(++idx)); + return new TriadCoords(x, y, z); } /** @@ -128,33 +111,18 @@ public ChipLocation asChipLocation() { int rootX = x * TRIAD_CHIP_SIZE; int rootY = y * TRIAD_CHIP_SIZE; switch (z) { - case 1: + case 1 -> { rootX += TRIAD_MAJOR_OFFSET; rootY += TRIAD_MINOR_OFFSET; - break; - case 2: + } + case 2 -> { rootX += TRIAD_MINOR_OFFSET; rootY += TRIAD_MAJOR_OFFSET; - break; - case 0: - default: - break; } - return new ChipLocation(rootX, rootY); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof TriadCoords) { - var other = (TriadCoords) obj; - return x == other.x && y == other.y && z == other.z; + default -> { } - return false; - } - - @Override - public int hashCode() { - return x * 25 + y * 5 + z; + } + return new ChipLocation(rootX, rootY); } @Override @@ -194,32 +162,28 @@ TriadCoords deserializeArray() throws IOException { @Override TriadCoords deserializeObject() throws IOException { - Integer x = null, y = null, z = null; + var x = new ValueHolder(); + var y = new ValueHolder(); + var z = new ValueHolder(); String name; while ((name = getNextFieldName()) != null) { switch (name) { - case "x": - x = requireSetOnceInt(name, x); - break; - case "y": - y = requireSetOnceInt(name, y); - break; - case "z": - z = requireSetOnceInt(name, z); - break; - default: - unknownProperty(name); + case "x" -> requireSetOnceInt(name, x); + case "y" -> requireSetOnceInt(name, y); + case "z" -> requireSetOnceInt(name, z); + default -> unknownProperty(name); } } - if (x == null || y == null || z == null) { - missingProperty("x", x, "y", y, "z", z); + if (x.isEmpty() || y.isEmpty() || z.isEmpty()) { + missingProperty("x", x.getValue(), "y", y.getValue(), "z", + z.getValue()); } - return new TriadCoords(x, y, z); + return new TriadCoords(x.getValue(), y.getValue(), z.getValue()); } @Override TriadCoords deserializeString(String string) { - return new TriadCoords(string); + return TriadCoords.parse(string); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidBoardNumber.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidBoardNumber.java index df3f8f7037..2517f069fb 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidBoardNumber.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidBoardNumber.java @@ -26,11 +26,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.PositiveOrZero; /** * Validates that a board number is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidCabinetNumber.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidCabinetNumber.java index ea45d6c6fd..d74a4be228 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidCabinetNumber.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidCabinetNumber.java @@ -26,11 +26,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.PositiveOrZero; /** * Validates that a cabinet number is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidFrameNumber.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidFrameNumber.java index ddbbf3b3a9..d5c98e231d 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidFrameNumber.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidFrameNumber.java @@ -26,11 +26,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.PositiveOrZero; /** * Validates that a frame number is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadDepth.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadDepth.java index 1c69089191..31825327fc 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadDepth.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadDepth.java @@ -25,11 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.Positive; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Positive; /** * Validates that a machine's depth is in a sane range. Note that only machines diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadHeight.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadHeight.java index b5fd02f9de..2c8136495a 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadHeight.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadHeight.java @@ -25,11 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.Positive; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Positive; /** * Validates that a machine's height (in triads) is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadWidth.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadWidth.java index ff64ef02cd..bb836b7bd8 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadWidth.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadWidth.java @@ -25,11 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.Positive; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Positive; /** * Validates that a machine's width (in triads) is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadX.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadX.java index 66afea3517..d0732fe5ab 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadX.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadX.java @@ -25,11 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.PositiveOrZero; /** * Validates that a triad X coordinate is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadY.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadY.java index adaeea289d..c1f20dbfdd 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadY.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadY.java @@ -25,11 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.PositiveOrZero; /** * Validates that a triad Y coordinate is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadZ.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadZ.java index d92c9324da..d1f87f4781 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadZ.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/ValidTriadZ.java @@ -25,11 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.Payload; -import javax.validation.ReportAsSingleViolation; -import javax.validation.constraints.Max; -import javax.validation.constraints.PositiveOrZero; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import jakarta.validation.ReportAsSingleViolation; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.PositiveOrZero; /** * Validates that a triad Z coordinate is in a sane range. diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/package-info.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/package-info.java index 77f381d89a..517818ca81 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/package-info.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/board/package-info.java @@ -17,5 +17,25 @@ * Classes relating to boards in a SpiNNaker machine. Note that boards * themselves mostly not modelled, but their coordinates schemes are because * they are used in many places. + *

+ * Physically, cabinets contain a collection of frames, and frames + * contain a collection of boards. There is one controller BMP per frame; only + * that BMP may talk to the other BMPs in the frame. When boards are not put in + * a frame, their BMPs may be directly addressed (though in that situation there + * are not usually more than three boards connected). {@linkplain PhysicalCoords + * Physical coordinates} are typically only useful for when dealing with the + * hardware itself; however, {@linkplain BMPLocation BMP coordinates} are + * closely related. + *

+ * In large machines, boards are logically grouped into + * {@linkplain TriadCoords triads} for allocation control purposes; most + * allocations are done in terms of triads (except for single-board allocations) + * in order to keep the allocation algorithms reasonably tractable. The boards + * of a triad may not necessarily be all on the same frame, but they + * will be closely connected to one another. + *

+ * The actual connectivity setup is done by + * SpiNNer. See + * that project for details. */ package uk.ac.manchester.spinnaker.machine.board; diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/AbstractDataLink.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/AbstractDataLink.java index 0f4d7f2a28..29c7deab45 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/AbstractDataLink.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/AbstractDataLink.java @@ -18,10 +18,9 @@ import java.net.InetAddress; import java.util.Objects; -import javax.validation.Valid; - import com.google.errorprone.annotations.Immutable; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.Direction; import uk.ac.manchester.spinnaker.machine.HasChipLocation; @@ -32,6 +31,7 @@ * @author Christian-B */ @Immutable +// Should be sealed, but tests interfere public abstract class AbstractDataLink implements HasChipLocation { /** IP address of the data link on the board. */ public final InetAddress boardAddress; diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/FPGALinkData.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/FPGALinkData.java index 937b1b4d9d..2d3a3ad6b7 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/FPGALinkData.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/FPGALinkData.java @@ -65,12 +65,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (obj instanceof FPGALinkData) { - var other = (FPGALinkData) obj; - return sameAs(other) && (fpgaLinkId == other.fpgaLinkId) - && (fpgaId == other.fpgaId); - } - return false; + return (obj instanceof FPGALinkData other) && sameAs(other) + && (fpgaLinkId == other.fpgaLinkId) && (fpgaId == other.fpgaId); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/InetIdTuple.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/InetIdTuple.java index 3b67404ecd..a1fb3965ad 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/InetIdTuple.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/InetIdTuple.java @@ -16,7 +16,6 @@ package uk.ac.manchester.spinnaker.machine.datalinks; import java.net.InetAddress; -import java.util.Objects; import com.google.errorprone.annotations.Immutable; @@ -24,39 +23,11 @@ * A tuple of an IP address and a SpiNNaker link ID. * * @author Christian-B + * @param address + * The IP address of this tuple, which may be {@code null}. + * @param id + * The ID of this tuple. */ @Immutable -public final class InetIdTuple { - /** The InetAddress of this tuple which may be {@code null}. */ - public final InetAddress address; - - /** The ID of this tuple. */ - public final int id; - - /** - * Make an instance. - * - * @param address - * The IP address of this tuple, which may be {@code null}. - * @param id - * The ID of this tuple. - */ - public InetIdTuple(InetAddress address, int id) { - this.address = address; - this.id = id; - } - - @Override - public int hashCode() { - return Objects.hash(address, id); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof InetIdTuple) { - var other = (InetIdTuple) obj; - return (id == other.id) && Objects.equals(address, other.address); - } - return false; - } +public record InetIdTuple(InetAddress address, int id) { } diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/SpinnakerLinkData.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/SpinnakerLinkData.java index 20170d1ee2..44b3d99622 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/SpinnakerLinkData.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/datalinks/SpinnakerLinkData.java @@ -59,11 +59,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (obj instanceof SpinnakerLinkData) { - var other = (SpinnakerLinkData) obj; - return sameAs(other) && (spinnakerLinkId == other.spinnakerLinkId); - } - return false; + return (obj instanceof SpinnakerLinkData other) && sameAs(other) + && (spinnakerLinkId == other.spinnakerLinkId); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/reports/Reports.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/reports/Reports.java index e1ac403938..db7dad3630 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/reports/Reports.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/reports/Reports.java @@ -29,7 +29,6 @@ import org.slf4j.Logger; import uk.ac.manchester.spinnaker.machine.ChipLocation; -import uk.ac.manchester.spinnaker.machine.Link; import uk.ac.manchester.spinnaker.machine.Machine; /** @@ -97,7 +96,7 @@ private static void writeChipRouterReport(PrintWriter f, Machine machine, f.printf("Neighbouring chips:\n\t%s\n", chip.router.neighbouringChipsCoords()); f.println("Router list of links for this chip are:"); - for (Link link : chip.router.links()) { + for (var link : chip.router.links()) { f.printf("\t%s\n", link); } f.println("\t\t=========================="); diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/IPTag.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/IPTag.java index b9d847f6d9..0134e31be9 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/IPTag.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/IPTag.java @@ -22,10 +22,9 @@ import java.net.UnknownHostException; import java.util.Objects; -import javax.validation.Valid; - import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.ChipLocation; /** @@ -240,14 +239,10 @@ public ChipLocation getDestination() { @Override public boolean equals(Object o) { - if (o instanceof IPTag) { - var otherTag = (IPTag) o; - return partialEquals(otherTag) - && ipAddress.equals(otherTag.ipAddress) - && stripSDP == otherTag.stripSDP - && destination.equals(otherTag.destination); - } - return false; + return (o instanceof IPTag otherTag) && partialEquals(otherTag) + && ipAddress.equals(otherTag.ipAddress) + && stripSDP == otherTag.stripSDP + && destination.equals(otherTag.destination); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/ReverseIPTag.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/ReverseIPTag.java index 2c239d43a7..7fcc9c255b 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/ReverseIPTag.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/ReverseIPTag.java @@ -19,8 +19,7 @@ import java.net.ServerSocket; import java.util.Objects; -import javax.validation.Valid; - +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.machine.HasCoreLocation; import uk.ac.manchester.spinnaker.utils.UsedInJavadocOnly; @@ -98,12 +97,9 @@ public int getSdpPort() { @Override public boolean equals(Object o) { - if (o instanceof ReverseIPTag) { - var otherTag = (ReverseIPTag) o; - return partialEquals(otherTag) && (sdpPort == otherTag.sdpPort) - && destination.equals(otherTag.destination); - } - return false; + return (o instanceof ReverseIPTag otherTag) && partialEquals(otherTag) + && (sdpPort == otherTag.sdpPort) + && destination.equals(otherTag.destination); } @Override diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/Tag.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/Tag.java index 38a249a0ec..e49958e370 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/Tag.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/Tag.java @@ -20,12 +20,12 @@ import java.net.InetAddress; import java.util.Objects; -import javax.validation.constraints.NotNull; - +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.utils.validation.UDPPort; /** Common properties of SpiNNaker IP tags and reverse IP tags. */ -public abstract class Tag implements Comparable { +public abstract sealed class Tag + implements Comparable permits IPTag, ReverseIPTag { /** The board address associated with this tagID. */ @NotNull private final InetAddress boardAddress; diff --git a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/TagID.java b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/TagID.java index 27bbcebce1..67415a578d 100644 --- a/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/TagID.java +++ b/SpiNNaker-machine/src/main/java/uk/ac/manchester/spinnaker/machine/tags/TagID.java @@ -26,10 +26,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; /** * Validates that a number looks like a tag identifier. Always accepts diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestChip.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestChip.java index 93976ba007..821584b865 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestChip.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestChip.java @@ -187,7 +187,7 @@ public void testDefault2() throws UnknownHostException { public void testLinksVirtualMachine() throws UnknownHostException { var chip = new Chip(LOCATION_00, createRouter(), createInetAddress(), LOCATION_11); - final var values = chip.router.links(); + var values = chip.router.links(); assertEquals(2, values.size()); assertThrows(UnsupportedOperationException.class, () -> { values.remove(LINK_00_01); diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestRegionLocation.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestRegionLocation.java index 6d70fd1f21..2f3f92c94c 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestRegionLocation.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestRegionLocation.java @@ -31,7 +31,7 @@ public void testBasicUse() { assertEquals(2, r1.getX()); assertEquals(4, r1.getY()); assertEquals(6, r1.getP()); - assertEquals(8, r1.region); + assertEquals(8, r1.region()); assertEquals(r1, r1); assertNotEquals(r1, "r1"); diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestRouter.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestRouter.java index 0e02651888..964dad9135 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestRouter.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestRouter.java @@ -57,7 +57,7 @@ public void testRouterBasicUse() { public void testLinks() { var links = List.of(LINK00_01); var router = new Router(links); - final var values = router.links(); + var values = router.links(); assertEquals(1, values.size()); assertThrows(UnsupportedOperationException.class, () -> { values.remove(LINK00_01); diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestSpiNNakerTriadGeometry.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestSpiNNakerTriadGeometry.java index 5a11d12e06..54f578880f 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestSpiNNakerTriadGeometry.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestSpiNNakerTriadGeometry.java @@ -192,7 +192,7 @@ public void testSingleBoard() { @Test public void testSingleBoardForEach() { var instance = SpiNNakerTriadGeometry.getSpinn5Geometry(); - final var count = new Counter(); + var count = new Counter(); instance.singleBoardForEach(chip -> { count.increment(); assertEquals(ChipLocation.ZERO_ZERO, diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestVirtualMachine.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestVirtualMachine.java index 6b1ace7fe7..81b9e391a9 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestVirtualMachine.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/TestVirtualMachine.java @@ -60,7 +60,7 @@ public void testSmallBoards() { var bad = MachineDefaults.FOUR_CHIP_DOWN_LINKS .get(chip.asChipLocation()); for (var link : chip.router) { - assertThat(bad, not(hasItems(link.sourceLinkDirection))); + assertThat(bad, not(hasItems(link.sourceLinkDirection()))); } } } diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestChipDetails.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestChipDetails.java index 3bcdf98b12..b0f85a7baf 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestChipDetails.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestChipDetails.java @@ -26,8 +26,14 @@ public class TestChipDetails { @Test public void testFromJson() throws IOException { - var json = "{\"cores\": 18, \"deadLinks\": [3, 4, 5], " - + "\"ipAddress\": \"130.88.192.243\", \"ethernet\":[0, 0]}"; + var json = """ + { + "cores": 18, + "deadLinks": [3, 4, 5], + "ipAddress": "130.88.192.243", + "ethernet": [0, 0] + } + """; /* * String json = "{\"cores\": 18, \"ipAddress\": \"130.88.192.243\", * \"ethernet\":[0, 0]}"; diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestChipbean.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestChipbean.java index fed50c414b..a68232fcb2 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestChipbean.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestChipbean.java @@ -27,9 +27,16 @@ public class TestChipbean { @Test public void testFromJson() throws IOException { - var json = "[1, 2, {\"cores\": 17, \"ethernet\": [2, 3]}, {" - + "\"sdram\": 123469692, " - + "\"routerEntries\": 1013, \"monitors\": 2}]"; + var json = """ + [1, 2, { + "cores": 17, + "ethernet": [2, 3] + }, { + "sdram": 123469692, + "routerEntries": 1013, + "monitors": 2 + }] + """; var mapper = MapperFactory.createMapper(); var fromJson = mapper.readValue(json, ChipBean.class); assertNotNull(fromJson); diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestMachineBean.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestMachineBean.java index 13fa2eb9fa..a05bf3ca0e 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestMachineBean.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/bean/TestMachineBean.java @@ -56,8 +56,8 @@ public void testSevenEight() throws IOException { var fromJson = mapper.readValue(url, MachineBean.class); var machine = new Machine(fromJson); - assertEquals(24, machine.machineDimensions.height); - assertEquals(12, machine.machineDimensions.width); + assertEquals(24, machine.machineDimensions.height()); + assertEquals(12, machine.machineDimensions.width()); assertNotNull(machine); } diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/datalinks/TestAbstractDataLink.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/datalinks/TestAbstractDataLink.java index f5d6570104..9a142b1e49 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/datalinks/TestAbstractDataLink.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/datalinks/TestAbstractDataLink.java @@ -62,8 +62,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { - return (obj instanceof AbstractDataLink) - && sameAs((AbstractDataLink) obj); + return (obj instanceof AbstractDataLink link) && sameAs(link); } } } diff --git a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/tags/TestIPTag.java b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/tags/TestIPTag.java index 043e6234e4..d238b6900f 100644 --- a/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/tags/TestIPTag.java +++ b/SpiNNaker-machine/src/test/java/uk/ac/manchester/spinnaker/machine/tags/TestIPTag.java @@ -186,12 +186,18 @@ public void testEquals() throws UnknownHostException { @Test public void testFromJson() throws IOException { - var json = " {\"x\": 0, \"y\": 0, " - + "\"boardAddress\": \"192.168.240.253\", " - + "\"targetAddress\": \"localhost\", \"stripSDP\": true, " - + "\"tagID\": 1, \"trafficIdentifier\": \"DATA_SPEED_UP\"}"; var mapper = MapperFactory.createMapper(); - var fromJson = mapper.readValue(json, IPTag.class); + var fromJson = mapper.readValue(""" + { + "x": 0, + "y": 0, + "boardAddress": "192.168.240.253", + "targetAddress": "localhost", + "stripSDP": true, + "tagID": 1, + "trafficIdentifier": "DATA_SPEED_UP" + } + """, IPTag.class); assertNotNull(fromJson); } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/DirectPyNNJobParametersFactory.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/DirectPyNNJobParametersFactory.java index 3fdf8437cf..bd39780a62 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/DirectPyNNJobParametersFactory.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/DirectPyNNJobParametersFactory.java @@ -29,10 +29,10 @@ * A {@link JobParametersFactory} that uses the {@code experimentDescription} * itself as a PyNN script. */ -class DirectPyNNJobParametersFactory extends JobParametersFactory { +final class DirectPyNNJobParametersFactory extends JobParametersFactory { @Override - public JobParameters getJobParameters(final Job job, - final File workingDirectory, final String setupScript) + public JobParameters getJobParameters(Job job, File workingDirectory, + String setupScript) throws UnsupportedJobException, JobParametersFactoryException { if (!job.getCode().contains("import")) { throw new UnsupportedJobException(); @@ -40,9 +40,9 @@ public JobParameters getJobParameters(final Job job, try { return constructParameters(job, workingDirectory, setupScript); - } catch (final IOException e) { + } catch (IOException e) { throw new JobParametersFactoryException("Error storing script", e); - } catch (final Throwable e) { + } catch (Throwable e) { throw new JobParametersFactoryException( "General error with PyNN Script", e); } @@ -61,10 +61,9 @@ public JobParameters getJobParameters(final Job job, * @throws IOException * If the file can't be found to write */ - private JobParameters constructParameters(final Job job, - final File workingDirectory, final String setupScript) - throws IOException { - final var scriptFile = new File(workingDirectory, DEFAULT_SCRIPT_NAME); + private JobParameters constructParameters(Job job, File workingDirectory, + String setupScript) throws IOException { + var scriptFile = new File(workingDirectory, DEFAULT_SCRIPT_NAME); try (var writer = new PrintWriter(scriptFile, UTF_8)) { writer.print(job.getCode()); } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/GitPyNNJobParametersFactory.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/GitPyNNJobParametersFactory.java index 5386c69de9..ff99a08224 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/GitPyNNJobParametersFactory.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/GitPyNNJobParametersFactory.java @@ -36,13 +36,13 @@ * repository must be world-readable, or sufficient credentials must be present * in the URL. */ -class GitPyNNJobParametersFactory extends JobParametersFactory { +final class GitPyNNJobParametersFactory extends JobParametersFactory { @Override - public JobParameters getJobParameters(final Job job, - final File workingDirectory, final String setupScript) + public JobParameters getJobParameters(Job job, File workingDirectory, + String setupScript) throws UnsupportedJobException, JobParametersFactoryException { // Test that there is a URL - final var jobCodeLocation = job.getCode().trim(); + var jobCodeLocation = job.getCode().trim(); if (!jobCodeLocation.startsWith("http://") && !jobCodeLocation.startsWith("https://")) { throw new UnsupportedJobException(); @@ -52,13 +52,13 @@ public JobParameters getJobParameters(final Job job, try { return constructParameters(job, workingDirectory, jobCodeLocation, setupScript); - } catch (final InvalidRemoteException e) { + } catch (InvalidRemoteException e) { throw new JobParametersFactoryException("Remote is not valid", e); - } catch (final TransportException e) { + } catch (TransportException e) { throw new JobParametersFactoryException("Transport failed", e); - } catch (final GitAPIException e) { + } catch (GitAPIException e) { throw new JobParametersFactoryException("Error using Git", e); - } catch (final Throwable e) { + } catch (Throwable e) { throw new JobParametersFactoryException( "General error getting git repository", e); } @@ -85,12 +85,11 @@ public JobParameters getJobParameters(final Job job, * @throws URISyntaxException * If the URI syntax is incorrect */ - private JobParameters constructParameters(final Job job, - final File workingDirectory, final String experimentDescription, - final String setupScript) + private JobParameters constructParameters(Job job, File workingDirectory, + String experimentDescription, String setupScript) throws GitAPIException, InvalidRemoteException, TransportException, URISyntaxException { - final var clone = cloneRepository(); + var clone = cloneRepository(); var urish = new URIish(experimentDescription); if (nonNull(urish.getUser())) { var pass = urish.getPass(); @@ -98,7 +97,8 @@ private JobParameters constructParameters(final Job job, pass = ""; } clone.setCredentialsProvider( - new UsernamePasswordCredentialsProvider(urish.getUser(), pass)); + new UsernamePasswordCredentialsProvider(urish.getUser(), + pass)); } // Clone into a sub-directory of the working directory @@ -106,7 +106,7 @@ private JobParameters constructParameters(final Job job, if (subdir.equals("")) { subdir = "repo"; } - final var cloneDir = new File(workingDirectory, subdir); + var cloneDir = new File(workingDirectory, subdir); clone.setURI(experimentDescription); clone.setDirectory(cloneDir); @@ -114,12 +114,12 @@ private JobParameters constructParameters(final Job job, clone.call(); var script = DEFAULT_SCRIPT_NAME + SYSTEM_ARG; - final var command = job.getCommand(); + var command = job.getCommand(); if (nonNull(command) && !command.isEmpty()) { script = command; } - return new PyNNJobParameters(cloneDir.getAbsolutePath(), - setupScript, script, job.getHardwareConfig()); + return new PyNNJobParameters(cloneDir.getAbsolutePath(), setupScript, + script, job.getHardwareConfig()); } } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/JobParametersFactory.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/JobParametersFactory.java index 0334be2080..5bb7946e8c 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/JobParametersFactory.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/JobParametersFactory.java @@ -27,7 +27,9 @@ /** * A factory that produces job parameters. */ -public abstract class JobParametersFactory { +public abstract sealed class JobParametersFactory + permits DirectPyNNJobParametersFactory, GitPyNNJobParametersFactory, + ZipPyNNJobParametersFactory { /** * The argument to append to the script name to request that the system is * added to the command line. @@ -78,19 +80,19 @@ public abstract JobParameters getJobParameters(Job job, * @return The parameters, or {@code null} if the parameters can't be * generated. */ - public static JobParameters getJobParameters(final Job job, - final File workingDirectory, final String setupScript, - final Map errors) { - for (final var factory : JOB_PARAMETER_FACTORIES) { + public static JobParameters getJobParameters(Job job, File workingDirectory, + String setupScript, + Map errors) { + for (var factory : JOB_PARAMETER_FACTORIES) { try { - final var parameters = factory.getJobParameters(job, - workingDirectory, setupScript); + var parameters = factory.getJobParameters(job, workingDirectory, + setupScript); if (nonNull(parameters)) { return parameters; } - } catch (final UnsupportedJobException e) { + } catch (UnsupportedJobException e) { // Do Nothing - } catch (final JobParametersFactoryException e) { + } catch (JobParametersFactoryException e) { errors.put(factory.getClass().getSimpleName(), e); } } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/JobParametersFactoryException.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/JobParametersFactoryException.java index f926779982..b2375b35b6 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/JobParametersFactoryException.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/JobParametersFactoryException.java @@ -29,7 +29,7 @@ public class JobParametersFactoryException extends Exception { * @param message * The message */ - public JobParametersFactoryException(final String message) { + public JobParametersFactoryException(String message) { super(message); } @@ -41,8 +41,7 @@ public JobParametersFactoryException(final String message) { * @param cause * The cause */ - public JobParametersFactoryException(final String message, - final Throwable cause) { + public JobParametersFactoryException(String message, Throwable cause) { super(message, cause); } } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/ZipPyNNJobParametersFactory.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/ZipPyNNJobParametersFactory.java index 73966730c2..cba1d64682 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/ZipPyNNJobParametersFactory.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/job_parameters/ZipPyNNJobParametersFactory.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.util.List; import org.rauschig.jarchivelib.ArchiveFormat; import org.rauschig.jarchivelib.CompressionType; @@ -39,13 +40,13 @@ * file. The URL must refer to a world-readable URL or the credentials must be * present in the URL. */ -class ZipPyNNJobParametersFactory extends JobParametersFactory { +final class ZipPyNNJobParametersFactory extends JobParametersFactory { @Override - public JobParameters getJobParameters(final Job job, - final File workingDirectory, final String setupScript) + public JobParameters getJobParameters(Job job, File workingDirectory, + String setupScript) throws UnsupportedJobException, JobParametersFactoryException { // Test that there is a URL - final var jobCodeLocation = job.getCode().trim(); + var jobCodeLocation = job.getCode().trim(); if (!jobCodeLocation.startsWith("http://") && !jobCodeLocation.startsWith("https://")) { throw new UnsupportedJobException(); @@ -55,18 +56,18 @@ public JobParameters getJobParameters(final Job job, URL url; try { url = new URL(jobCodeLocation); - } catch (final MalformedURLException e) { + } catch (MalformedURLException e) { throw new JobParametersFactoryException("The URL is malformed", e); } // Try to get the file and extract it try { return constructParameters(job, workingDirectory, url, setupScript); - } catch (final IOException e) { + } catch (IOException e) { log(e); throw new JobParametersFactoryException( "Error in communication or extraction", e); - } catch (final Throwable e) { + } catch (Throwable e) { log(e); throw new JobParametersFactoryException( "General error with zip extraction", e); @@ -74,8 +75,8 @@ public JobParameters getJobParameters(final Job job, } /** The supported compression types. */ - private static final CompressionType[] SUPPORTED_TYPES = - new CompressionType[]{BZIP2, GZIP}; + private static final List SUPPORTED_TYPES = + List.of(BZIP2, GZIP); /** * Extract an archive using auto-detection for the format. @@ -88,13 +89,13 @@ public JobParameters getJobParameters(final Job job, * @throws IOException * If there is a general error in extraction */ - private boolean extractAutodetectedArchive(final File output, - final File workingDirectory) throws IOException { + private boolean extractAutodetectedArchive(File output, + File workingDirectory) throws IOException { try { - final var archiver = createArchiver(output); + var archiver = createArchiver(output); archiver.extract(output, workingDirectory); return true; - } catch (final IllegalArgumentException e) { + } catch (IllegalArgumentException e) { return false; } } @@ -108,14 +109,14 @@ private boolean extractAutodetectedArchive(final File output, * The archive to extract * @return True if the archive was extracted, False otherwise */ - private boolean extractArchiveUsingKnownFormats(final File workingDirectory, - final File output) { - for (final var format : ArchiveFormat.values()) { + private boolean extractArchiveUsingKnownFormats(File workingDirectory, + File output) { + for (var format : ArchiveFormat.values()) { try { - final var archiver = createArchiver(format); + var archiver = createArchiver(format); archiver.extract(output, workingDirectory); return true; - } catch (final IOException e) { + } catch (IOException e) { // Ignore - try the next } } @@ -131,15 +132,14 @@ private boolean extractArchiveUsingKnownFormats(final File workingDirectory, * The archive to extract * @return True if the archive was extracted, False otherwise */ - private boolean extractTypedArchive(final File workingDirectory, - final File output) { - for (final var format : ArchiveFormat.values()) { - for (final var type : SUPPORTED_TYPES) { + private boolean extractTypedArchive(File workingDirectory, File output) { + for (var format : ArchiveFormat.values()) { + for (var type : SUPPORTED_TYPES) { try { - final var archiver = createArchiver(format, type); + var archiver = createArchiver(format, type); archiver.extract(output, workingDirectory); return true; - } catch (final IOException e) { + } catch (IOException e) { // Ignore - try the next } } @@ -164,11 +164,10 @@ private boolean extractTypedArchive(final File workingDirectory, * @throws JobParametersFactoryException * If no way to uncompress the file could be found */ - private JobParameters constructParameters(final Job job, - final File workingDirectory, final URL url, - final String setupScript) + private JobParameters constructParameters(Job job, File workingDirectory, + URL url, String setupScript) throws IOException, JobParametersFactoryException { - final var output = downloadFile(url, workingDirectory, null); + var output = downloadFile(url, workingDirectory, null); /* Test if there is a recognised archive */ boolean archiveExtracted = @@ -203,7 +202,7 @@ private JobParameters constructParameters(final Job job, } var script = DEFAULT_SCRIPT_NAME + SYSTEM_ARG; - final var command = job.getCommand(); + var command = job.getCommand(); if (nonNull(command) && !command.isEmpty()) { script = command; } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/JobProcessFactory.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/JobProcessFactory.java index 8505fc879c..2df7e9eba9 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/JobProcessFactory.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/JobProcessFactory.java @@ -39,7 +39,7 @@ public class JobProcessFactory { * The thread group for the factory. All threads created by the * factory will be within this group. */ - public JobProcessFactory(final ThreadGroup threadGroup) { + public JobProcessFactory(ThreadGroup threadGroup) { this.threadGroup = threadGroup; } @@ -50,7 +50,7 @@ public JobProcessFactory(final ThreadGroup threadGroup) { * The name of the thread group for the factory. All threads * created by the factory will be within this group. */ - public JobProcessFactory(final String threadGroupName) { + public JobProcessFactory(String threadGroupName) { this(new ThreadGroup(threadGroupName)); } @@ -86,8 +86,8 @@ public interface ProcessSupplier

* The job process supplier */ public

void addMapping( - final Class

parameterType, - final ProcessSupplier

processSupplier) { + Class

parameterType, + ProcessSupplier

processSupplier) { typeMap.put(parameterType, processSupplier); } @@ -112,20 +112,19 @@ public Collection> getParameterTypes() { * If the type of the job parameters is unexpected. */ public

JobProcess

createProcess( - final P parameters) { + P parameters) { /* * We know that this is of the correct type, because the addMapping * method will only allow the correct type mapping in */ @SuppressWarnings("unchecked") - final var supplier = - (ProcessSupplier

) typeMap.get(parameters.getClass()); + var supplier = (ProcessSupplier

) typeMap.get(parameters.getClass()); if (isNull(supplier)) { throw new IllegalArgumentException( "unsupported job parameter type: " + parameters.getClass()); } - final var process = supplier.get(); + var process = supplier.get(); // Magically set the thread group if there is one setField(process, "threadGroup", threadGroup); return process; @@ -142,10 +141,10 @@ public

JobProcess

createProcess( * The value to set the field to */ @SuppressWarnings("unused") - private static void setField(final Class clazz, final String fieldName, - final Object value) { + private static void setField(Class clazz, String fieldName, + Object value) { try { - final var threadGroupField = clazz.getDeclaredField(fieldName); + var threadGroupField = clazz.getDeclaredField(fieldName); threadGroupField.setAccessible(true); threadGroupField.set(null, value); } catch (NoSuchFieldException | SecurityException @@ -164,10 +163,10 @@ private static void setField(final Class clazz, final String fieldName, * @param value * The value to set */ - private static void setField(final Object instance, final String fieldName, - final Object value) { + private static void setField(Object instance, String fieldName, + Object value) { try { - final var threadGroupField = + var threadGroupField = instance.getClass().getDeclaredField(fieldName); threadGroupField.setAccessible(true); threadGroupField.set(instance, value); diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/ProvenanceItem.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/ProvenanceItem.java index 3b3bd7686f..65afffec1f 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/ProvenanceItem.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/ProvenanceItem.java @@ -35,7 +35,7 @@ public class ProvenanceItem { * @param value * The content of the value. */ - public ProvenanceItem(final List path, final String value) { + public ProvenanceItem(List path, String value) { this.path = path; this.value = value; } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/PyNNJobProcess.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/PyNNJobProcess.java index b4a614402c..f299591d2b 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/PyNNJobProcess.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocess/PyNNJobProcess.java @@ -41,7 +41,6 @@ import java.io.Reader; import java.io.StringWriter; import java.util.ArrayList; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -82,10 +81,11 @@ public class PyNNJobProcess implements JobProcess { private static final int FINALIZATION_DELAY = 1000; /** The set of ignored file extensions in the outputs. */ - private static final Set IGNORED_EXTENSIONS = new HashSet<>(); + private static final Set IGNORED_EXTENSIONS = Set.of("pyc"); /** The set of ignored directories in the outputs. */ - private static final Set IGNORED_DIRECTORIES = new HashSet<>(); + private static final Set IGNORED_DIRECTORIES = + Set.of("application_generated_data_files", "reports"); /** The timeout for running jobs, in hours. */ private static final int RUN_TIMEOUT = 7 * 24; @@ -97,20 +97,6 @@ public class PyNNJobProcess implements JobProcess { private static final Pattern ARGUMENT_FINDER = Pattern.compile("([^\"]\\S*|\".+?\")\\s*"); - static { - IGNORED_EXTENSIONS.add("pyc"); - IGNORED_DIRECTORIES.add("application_generated_data_files"); - IGNORED_DIRECTORIES.add("reports"); - } - - /** Provenance data items to be added to final provenance data. */ - private static final String[] PROVENANCE_ITEMS_TO_ADD = new String[]{ - "version_data/.*", "router_provenance/total_multi_cast_sent_packets", - "router_provenance/total_created_packets", - "router_provenance/total_dropped_packets", - "router_provenance/total_missed_dropped_packets", - "router_provenance/total_lost_dropped_packets"}; - /** The directory where the process is executed. */ private File workingDirectory = null; @@ -136,7 +122,7 @@ public class PyNNJobProcess implements JobProcess { * The directory to find files in. * @return The set of files found. */ - private static Set gatherFiles(final File directory) { + private static Set gatherFiles(File directory) { return new LinkedHashSet<>( listFiles(directory, fileFilter(), directoryFilter())); } @@ -149,7 +135,7 @@ private static Set gatherFiles(final File directory) { private static IOFileFilter fileFilter() { return new AbstractFileFilter() { @Override - public boolean accept(final File file) { + public boolean accept(File file) { return !IGNORED_EXTENSIONS .contains(getExtension(file.getName())); } @@ -164,7 +150,7 @@ public boolean accept(final File file) { private static IOFileFilter directoryFilter() { return new AbstractFileFilter() { @Override - public boolean accept(final File file) { + public boolean accept(File file) { return !IGNORED_DIRECTORIES.contains(file.getName()); } }; @@ -174,25 +160,25 @@ public boolean accept(final File file) { * Executes the process. */ @Override - public void execute(final String machineUrl, final SpinnakerMachine machine, - final PyNNJobParameters parameters, final LogWriter logWriter) { + public void execute(String machineUrl, SpinnakerMachine machine, + PyNNJobParameters parameters, LogWriter logWriter) { try { status = Running; workingDirectory = new File(parameters.getWorkingDirectory()); // Run the setup - final int setupValue = runSetup(parameters, logWriter); + int setupValue = runSetup(parameters, logWriter); if (setupValue != 0) { throw new Exception("Setup exited with non-zero error code + (" + setupValue + ")"); } // Create a spynnaker config file - final var cfgFile = new File(workingDirectory, "spynnaker.cfg"); + var cfgFile = new File(workingDirectory, "spynnaker.cfg"); // Add the details of the machine - final var ini = new Ini(); - final var config = ini.getConfig(); + var ini = new Ini(); + var config = ini.getConfig(); config.setEscape(false); config.setLowerCaseSection(false); config.setLowerCaseOption(false); @@ -200,7 +186,7 @@ public void execute(final String machineUrl, final SpinnakerMachine machine, ini.load(cfgFile); } - final Section section; + Section section; if (!ini.containsKey(SECTION)) { section = ini.add(SECTION); } else { @@ -209,7 +195,7 @@ public void execute(final String machineUrl, final SpinnakerMachine machine, if (nonNull(machine)) { section.put("machine_name", machine.getMachineName()); section.put("version", machine.getVersion()); - final var bmpDetails = machine.getBmpDetails(); + var bmpDetails = machine.getBmpDetails(); if (nonNull(bmpDetails)) { section.put("bmp_names", bmpDetails); } @@ -219,7 +205,7 @@ public void execute(final String machineUrl, final SpinnakerMachine machine, ini.store(cfgFile); // Keep existing files to compare to later - final var existingFiles = gatherFiles(workingDirectory); + var existingFiles = gatherFiles(workingDirectory); // Get a lifetime if there is one var hwConfig = parameters.getHardwareConfiguration(); @@ -230,15 +216,15 @@ public void execute(final String machineUrl, final SpinnakerMachine machine, } // Execute the program - final int exitValue = runSubprocess( + int exitValue = runSubprocess( parameters, logWriter, lifetimeHours); // Get the provenance data gatherProvenance(workingDirectory); // Get any output files - final var allFiles = gatherFiles(workingDirectory); - for (final var file : allFiles) { + var allFiles = gatherFiles(workingDirectory); + for (var file : allFiles) { if (!existingFiles.contains(file)) { outputs.add(file); } @@ -255,7 +241,7 @@ public void execute(final String machineUrl, final SpinnakerMachine machine, + exitValue + ")"); } status = Finished; - } catch (final Throwable e) { + } catch (Throwable e) { var stringWriter = new StringWriter(); var printWriter = new PrintWriter(stringWriter); e.printStackTrace(printWriter); @@ -273,22 +259,20 @@ public void execute(final String machineUrl, final SpinnakerMachine machine, * The parameters to the setup process. * @param logWriter * Where to send log messages. - * @return - * The exit value of the process + * @return The exit value of the process * @throws IOException - * If there was an error starting the process + * If there was an error starting the process * @throws InterruptedException - * If the process was interrupted before return + * If the process was interrupted before return */ - private int runSetup(final PyNNJobParameters parameters, - final LogWriter logWriter) + private int runSetup(PyNNJobParameters parameters, LogWriter logWriter) throws IOException, InterruptedException { - final var command = new ArrayList(); + var command = new ArrayList(); command.add(SETUP_RUNNER); command.add(parameters.getSetupScript()); // Build a process - final var builder = new ProcessBuilder(command); + var builder = new ProcessBuilder(command); builder.directory(workingDirectory); builder.redirectErrorStream(true); var mapper = new ObjectMapper(); @@ -297,15 +281,15 @@ private int runSetup(final PyNNJobParameters parameters, for (var entry: hardwareConfig.entrySet()) { String stringValue = null; var value = entry.getValue(); - if (value instanceof String) { - stringValue = (String) value; + if (value instanceof String sv) { + stringValue = sv; } else { stringValue = mapper.writeValueAsString(value); } builder.environment().put(entry.getKey(), stringValue); } } - final var process = builder.start(); + var process = builder.start(); // Run a thread to gather the log try (var logger = @@ -332,24 +316,22 @@ private int runSetup(final PyNNJobParameters parameters, * @throws InterruptedException * If the process was interrupted before return */ - private int runSubprocess(final PyNNJobParameters parameters, - final LogWriter logWriter, final int lifetime) - throws IOException, InterruptedException { - final var command = new ArrayList(); + private int runSubprocess(PyNNJobParameters parameters, LogWriter logWriter, + int lifetime) throws IOException, InterruptedException { + var command = new ArrayList(); command.add(SUBPROCESS_RUNNER); - final var scriptMatcher = - ARGUMENT_FINDER.matcher(parameters.getUserScript()); + var scriptMatcher = ARGUMENT_FINDER.matcher(parameters.getUserScript()); while (scriptMatcher.find()) { command.add( scriptMatcher.group(1).replace("{system}", "spiNNaker")); } - final var builder = new ProcessBuilder(command); + var builder = new ProcessBuilder(command); log("Running " + command + " in " + workingDirectory); builder.directory(workingDirectory); builder.redirectErrorStream(true); - final var process = builder.start(); + var process = builder.start(); // Run a thread to gather the log try (var logger = @@ -375,8 +357,8 @@ private int runSubprocess(final PyNNJobParameters parameters, * @throws InterruptedException * If the process was interrupted before return */ - private static int runProcess(final Process process, final int lifetime, - final TimeUnit lifetimeUnits) throws InterruptedException { + private static int runProcess(Process process, int lifetime, + TimeUnit lifetimeUnits) throws InterruptedException { if (!process.waitFor(lifetime, lifetimeUnits)) { process.destroy(); if (!process.waitFor(FINALIZATION_DELAY, MILLISECONDS)) { @@ -401,12 +383,10 @@ private static int runProcess(final Process process, final int lifetime, * @throws JAXBException * If anything goes wrong with deserialisation of the XML. */ - private void zipProvenance(final ZipOutputStream reportsZip, - final File directory, final String path) - throws IOException { - + private void zipProvenance(ZipOutputStream reportsZip, File directory, + String path) throws IOException { // Go through the report files and zip them up - for (final var file : directory.listFiles()) { + for (var file : directory.listFiles()) { if (file.isDirectory()) { zipProvenance(reportsZip, file, path + "/" + file.getName()); } else { @@ -429,12 +409,11 @@ private void zipProvenance(final ZipOutputStream reportsZip, * @throws JAXBException * If anything goes wrong with deserialisation of XML. */ - private void gatherProvenance(final File workingDirectory) + private void gatherProvenance(File workingDirectory) throws IOException { // Find the reports folder - final var reportsFolder = new File(workingDirectory, "reports"); + var reportsFolder = new File(workingDirectory, "reports"); if (reportsFolder.isDirectory()) { - // Create a zip file of the reports try (var reportsZip = new ZipOutputStream(new FileOutputStream( new File(workingDirectory, "reports.zip")))) { @@ -506,7 +485,7 @@ class ReaderLogWriter extends Thread implements AutoCloseable { * @param writer * The writer to write to */ - ReaderLogWriter(final Reader reader, final LogWriter writer) { + ReaderLogWriter(Reader reader, LogWriter writer) { super(threadGroup, "Reader Log Writer"); this.reader = buffer(reader); this.writer = requireNonNull(writer); @@ -522,7 +501,7 @@ class ReaderLogWriter extends Thread implements AutoCloseable { * @param writer * The writer to write to. */ - ReaderLogWriter(final InputStream input, final LogWriter writer) { + ReaderLogWriter(InputStream input, LogWriter writer) { this(new InputStreamReader(input), writer); } @@ -554,7 +533,7 @@ public void start() { */ private void copyStream() throws IOException { while (!interrupted()) { - final var line = reader.readLine(); + var line = reader.readLine(); if (isNull(line)) { return; } @@ -574,7 +553,7 @@ public void close() { while (running) { wait(); } - } catch (final InterruptedException e) { + } catch (InterruptedException e) { // Does Nothing } } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocessmanager/JobProcessManager.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocessmanager/JobProcessManager.java index 0ae3bc8691..dbd558bf4d 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocessmanager/JobProcessManager.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/jobprocessmanager/JobProcessManager.java @@ -23,6 +23,8 @@ import static java.util.Objects.requireNonNull; import static org.apache.commons.io.FileUtils.deleteQuietly; import static org.eclipse.jgit.util.FileUtils.createTempDir; +import static uk.ac.manchester.spinnaker.nmpi.model.job.JobManagerInterface.PATH; +import static uk.ac.manchester.spinnaker.nmpi.model.job.JobManagerInterface.SETUP_SCRIPT; import static uk.ac.manchester.spinnaker.nmpiexec.utils.FileDownloader.downloadFile; import static uk.ac.manchester.spinnaker.nmpiexec.utils.Log.log; @@ -44,7 +46,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import uk.ac.manchester.spinnaker.nmpi.model.job.JobManagerInterface; import uk.ac.manchester.spinnaker.nmpi.model.job.JobParameters; @@ -82,10 +84,11 @@ public static void main(String[] args) { * @param args * The command line arguments. * @throws IllegalArgumentException - * If an unrecognized argument is found. + * If an unrecognized argument is found. * @throws IOException - * If an authentication token can't be read. + * If an authentication token can't be read. */ + @Override public void run(String... args) throws IOException { String serverUrl = null; boolean deleteOnExit = false; @@ -96,27 +99,26 @@ public void run(String... args) throws IOException { for (int i = 0; i < args.length; i++) { switch (args[i]) { - case "--serverUrl" : + case "--serverUrl" -> { serverUrl = args[++i]; - break; - case "--executerId" : + } + case "--executerId" -> { executerId = args[++i]; - break; - case "--deleteOnExit" : + } + case "--deleteOnExit" -> { deleteOnExit = true; - break; - case "--local" : + } + case "--local" -> { isLocal = true; - break; - case "--liveUploadOutput" : + } + case "--liveUploadOutput" -> { liveUploadOutput = true; - break; - case "--requestMachine" : + } + case "--requestMachine" -> { requestMachine = true; - break; - default : - throw new IllegalArgumentException( - "unknown option: " + args[i]); + } + default -> throw new IllegalArgumentException( + "unknown option: " + args[i]); } } @@ -158,9 +160,7 @@ class UploadingJobManagerLogWriter extends JobManagerLogWriter { /** An object to synchronise on when sending data. */ private final Object sendSync = new Object(); - /** - * Make a log writer that uploads the log every half second. - */ + /** Make a log writer that uploads the log every half second. */ UploadingJobManagerLogWriter() { sendTimer = new Timer(UPDATE_INTERVAL, e -> sendLog()); } @@ -184,7 +184,7 @@ private void sendLog() { } @Override - public void append(final String logMsg) { + public void append(String logMsg) { log("Process Output: " + logMsg); synchronized (this) { sendTimer.restart(); @@ -249,9 +249,9 @@ public void stop() { * @param requestMachine * Whether to request a machine. */ - JobProcessRunner(final String serverUrl, final boolean deleteOnExit, - final boolean isLocal, final String executerId, - final boolean liveUploadOutput, final boolean requestMachine) { + JobProcessRunner(String serverUrl, boolean deleteOnExit, + boolean isLocal, String executerId, + boolean liveUploadOutput, boolean requestMachine) { this.serverUrl = requireNonNull( serverUrl, "--serverUrl must be specified"); this.executerId = requireNonNull( @@ -280,26 +280,25 @@ public void runJob() { log("Going to run job " + job.getId() + " in collab " + projectId); // Create a temporary location for the job - final var workingDirectory = createTempDir("job", ".tmp", null); + var workingDirectory = createTempDir("job", ".tmp", null); log("Running in temporary directory " + workingDirectory); // Download the setup script - var downloadUrl = serverUrl + JobManagerInterface.PATH - + "/" + JobManagerInterface.SETUP_SCRIPT; + var downloadUrl = serverUrl + PATH + "/" + SETUP_SCRIPT; log("Downloading setup script from " + downloadUrl); - final var setupScript = downloadFile(downloadUrl, - workingDirectory, JobManagerInterface.SETUP_SCRIPT); + var setupScript = + downloadFile(downloadUrl, workingDirectory, SETUP_SCRIPT); - final var parameters = getJobParameters( - workingDirectory, setupScript.getAbsolutePath()); + var parameters = getJobParameters(workingDirectory, + setupScript.getAbsolutePath()); // Create a process to process the request log("Creating process from parameters"); - final var process = JOB_PROCESS_FACTORY.createProcess(parameters); + var process = JOB_PROCESS_FACTORY.createProcess(parameters); logWriter = getLogWriter(); // Read the machine - final var machine = getMachine(); + var machine = getMachine(); // Execute the process log("Running job " + job.getId() + " on " + machine + " using " @@ -310,7 +309,7 @@ public void runJob() { // Get the exit status processOutcome(workingDirectory, process, logWriter.getLog()); - } catch (final Exception error) { + } catch (Exception error) { log(error); reportFailure(error); exit(1); @@ -323,7 +322,7 @@ public void runJob() { * @param error * The error of the failure. */ - private void reportFailure(final Throwable error) { + private void reportFailure(Throwable error) { if (isNull(jobManager) || isNull(job)) { log(error); return; @@ -340,8 +339,8 @@ private void reportFailure(final Throwable error) { message = "No Error Message"; } jobManager.setJobError(projectId, job.getId(), message, log, "", - new ArrayList(), new RemoteStackTrace(error)); - } catch (final Throwable t) { + List.of(), new RemoteStackTrace(error)); + } catch (Throwable t) { // Exception while reporting exception... log(t); log(error); @@ -377,11 +376,11 @@ private Machine getMachine() { * unreadable or the job being unsupported on the current * architectural configuration. */ - private JobParameters getJobParameters(final File workingDirectory, - final String setupScript) throws IOException { - final var errors = new HashMap(); - final var parameters = JobParametersFactory.getJobParameters( - job, workingDirectory, setupScript, errors); + private JobParameters getJobParameters(File workingDirectory, + String setupScript) throws IOException { + var errors = new HashMap(); + var parameters = JobParametersFactory.getJobParameters(job, + workingDirectory, setupScript, errors); if (isNull(parameters)) { if (!errors.isEmpty()) { @@ -394,7 +393,7 @@ private JobParameters getJobParameters(final File workingDirectory, // Get any requested input files if (nonNull(job.getInputData())) { - for (final var input : job.getInputData()) { + for (var input : job.getInputData()) { downloadFile(input.getUrl(), workingDirectory, null); } } @@ -426,15 +425,14 @@ private JobManagerLogWriter getLogWriter() { * @throws IOException * If there is an error reading or writing files */ - private void processOutcome(final File workingDirectory, - final JobProcess process, final String log) - throws IOException { - final var status = process.getStatus(); + private void processOutcome(File workingDirectory, JobProcess process, + String log) throws IOException { + var status = process.getStatus(); log("Process has finished with status " + status); - final var outputs = process.getOutputs(); - final var outputsAsStrings = new ArrayList(); - for (final var output : outputs) { + var outputs = process.getOutputs(); + var outputsAsStrings = new ArrayList(); + for (var output : outputs) { if (isLocal) { outputsAsStrings.add(output.getAbsolutePath()); } else { @@ -445,14 +443,14 @@ private void processOutcome(final File workingDirectory, } } - for (final var item : process.getProvenance()) { - jobManager.addProvenance( - job.getId(), item.getPath(), item.getValue()); + for (var item : process.getProvenance()) { + jobManager.addProvenance(job.getId(), item.getPath(), + item.getValue()); } switch (status) { - case Error : - final var error = process.getError(); + case Error -> { + var error = process.getError(); var message = error.getMessage(); if (isNull(message)) { message = "No Error Message"; @@ -460,8 +458,8 @@ private void processOutcome(final File workingDirectory, jobManager.setJobError(projectId, job.getId(), message, log, workingDirectory.getAbsolutePath(), outputsAsStrings, new RemoteStackTrace(error)); - break; - case Finished : + } + case Finished -> { jobManager.setJobFinished(projectId, job.getId(), log, workingDirectory.getAbsolutePath(), outputsAsStrings); @@ -470,9 +468,8 @@ private void processOutcome(final File workingDirectory, if (deleteOnExit) { deleteQuietly(workingDirectory); } - break; - default : - throw new IllegalStateException("Unknown status returned!"); + } + default -> throw new IllegalStateException("Unknown status returned!"); } } } @@ -493,7 +490,7 @@ class Machine { * @param machine * The machine object. */ - Machine(final SpinnakerMachine machine) { + Machine(SpinnakerMachine machine) { this.machine = machine; } @@ -505,7 +502,7 @@ class Machine { * @param id * The ID for the job. */ - Machine(final String baseUrl, final int id) { + Machine(String baseUrl, int id) { this.url = format("%sjob/%d/machine", baseUrl, id); } @@ -558,7 +555,7 @@ protected synchronized boolean isPopulated() { * @param message * The message to add */ - protected synchronized void appendCache(final String message) { + protected synchronized void appendCache(String message) { cached.append(message); } @@ -605,7 +602,7 @@ void stop() { */ class SimpleJobManagerLogWriter extends JobManagerLogWriter { @Override - public void append(final String logMsg) { + public void append(String logMsg) { log("Process Output: " + logMsg); synchronized (this) { appendCache(logMsg); @@ -630,12 +627,12 @@ class JobErrorsException extends IOException { * The errors to use. * @return An exception containing the errors. */ - private static String - buildMessage(final Map errors) { + private static String buildMessage( + Map errors) { var buffer = new StringWriter(); var bufferWriter = new PrintWriter(buffer); bufferWriter.println(MAIN_MSG); - for (final var key : errors.keySet()) { + for (var key : errors.keySet()) { bufferWriter.print(key); bufferWriter.println(":"); errors.get(key).printStackTrace(bufferWriter); @@ -650,8 +647,7 @@ class JobErrorsException extends IOException { * @param errors * The errors to build the exception from */ - JobErrorsException( - final Map errors) { + JobErrorsException(Map errors) { super(buildMessage(errors)); } } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/utils/FileDownloader.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/utils/FileDownloader.java index 52f947ad3e..0dce08c7ec 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/utils/FileDownloader.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/utils/FileDownloader.java @@ -16,6 +16,8 @@ package uk.ac.manchester.spinnaker.nmpiexec.utils; import static java.io.File.createTempFile; +import static java.net.HttpURLConnection.HTTP_MOVED_PERM; +import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.Files.copy; import static java.util.Objects.isNull; @@ -25,6 +27,7 @@ import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLDecoder; @@ -36,6 +39,9 @@ * Utilities for downloading a file. */ public abstract class FileDownloader { + /** Max number of HTTP redirects to follow. */ + private static final int REDIRECT_LIMIT = 5; + /** * Stops instantiation. */ @@ -50,9 +56,9 @@ private FileDownloader() { * The header * @return The filename */ - private static String getFileName(final String contentDisposition) { + private static String getFileName(String contentDisposition) { if (nonNull(contentDisposition)) { - final var cdl = contentDisposition.toLowerCase(); + var cdl = contentDisposition.toLowerCase(); if (cdl.startsWith("form-data") || cdl.startsWith("attachment")) { return new ContentDisposition(cdl).getFilename(); } @@ -71,16 +77,16 @@ private static String getFileName(final String contentDisposition) { * if an I/O error occurs * @return The created connection */ - private static URLConnection createConnectionWithAuth(final URL url, - final String userInfo) throws IOException { + private static URLConnection createConnectionWithAuth(URL url, + String userInfo) throws IOException { var urlConnection = requireNonNull(url).openConnection(); urlConnection.setDoInput(true); urlConnection.setRequestProperty("Accept", "*/*"); - if (nonNull(userInfo) && urlConnection instanceof HttpURLConnection) { - var httpConnection = (HttpURLConnection) urlConnection; - var basicAuth = "Basic " + Base64.encodeBase64URLSafeString( - userInfo.getBytes(UTF_8)); + if (nonNull(userInfo) + && urlConnection instanceof HttpURLConnection httpConnection) { + var basicAuth = "Basic " + Base64 + .encodeBase64URLSafeString(userInfo.getBytes(UTF_8)); httpConnection.setRequestProperty("Authorization", basicAuth); httpConnection.setInstanceFollowRedirects(false); } @@ -101,8 +107,8 @@ private static URLConnection createConnectionWithAuth(final URL url, * @throws IOException * If anything goes wrong. */ - public static File downloadFile(final URL url, final File workingDirectory, - final String defaultFilename) throws IOException { + public static File downloadFile(URL url, File workingDirectory, + String defaultFilename) throws IOException { requireNonNull(workingDirectory); // Open a connection @@ -110,37 +116,54 @@ public static File downloadFile(final URL url, final File workingDirectory, if (nonNull(userInfo)) { userInfo = URLDecoder.decode(url.getUserInfo(), UTF_8); } - var urlConnection = createConnectionWithAuth(url, userInfo); + var connection = createConnectionFolllowingRedirects(url, userInfo); - if (urlConnection instanceof HttpURLConnection) { - boolean redirect = false; - do { - redirect = false; - var httpConnection = (HttpURLConnection) urlConnection; + // Work out the output filename; uses the original URL for defaulting + var output = getTargetFile(url, workingDirectory, defaultFilename, + connection); + + // Write the file + copy(connection.getInputStream(), output.toPath()); + + return output; + } + + private static URLConnection createConnectionFolllowingRedirects(URL url, + String userInfo) throws IOException, MalformedURLException { + var connection = createConnectionWithAuth(url, userInfo); + boolean secure = url.getProtocol().equalsIgnoreCase("https"); + int redirectCount = 0; + boolean redirect; + var realUrl = url; + do { + if (connection instanceof HttpURLConnection httpConnection) { httpConnection.connect(); - int responseCode = httpConnection.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP - || responseCode == HttpURLConnection.HTTP_MOVED_PERM) { + redirect = switch (httpConnection.getResponseCode()) { + case HTTP_MOVED_TEMP, HTTP_MOVED_PERM -> { var location = httpConnection.getHeaderField("Location"); - if (isNull(location)) { + if (isNull(location) || location.isBlank()) { location = url.toString(); } - urlConnection = createConnectionWithAuth( - new URL(location), userInfo); - redirect = true; - + realUrl = new URL(realUrl, location); + if (secure && !realUrl.getProtocol() + .equalsIgnoreCase("https")) { + /* + * Jumped from HTTPS to some other protocol, so drop the + * credentials if we have them. + */ + userInfo = null; + } + connection = createConnectionWithAuth(realUrl, userInfo); + yield true; } - } while (redirect); - } - - // Work out the output filename - final var output = getTargetFile(url, workingDirectory, - defaultFilename, urlConnection); - - // Write the file - copy(urlConnection.getInputStream(), output.toPath()); - - return output; + default -> false; + }; + } else { + // Non-HTTP connections can't do redirects + redirect = false; + } + } while (redirect && redirectCount++ < REDIRECT_LIMIT); + return connection; } /** @@ -152,26 +175,34 @@ public static File downloadFile(final URL url, final File workingDirectory, * The directory to put the file in * @param defaultFilename * The default file name if nothing else can be used - * @param urlConnection + * @param connection * The connection where the file has been downloaded from * @return The file to write to. * @throws IOException * If the file cannot be created */ - private static File getTargetFile(final URL url, - final File workingDirectory, final String defaultFilename, - final URLConnection urlConnection) throws IOException { - final var filename = getFileName( - urlConnection.getHeaderField("Content-Disposition")); + private static File getTargetFile(URL url, File workingDirectory, + String defaultFilename, URLConnection connection) + throws IOException { + var filename = + getFileName(connection.getHeaderField("Content-Disposition")); if (nonNull(filename)) { + if (filename.isBlank() || filename.contains("..")) { + throw new IllegalArgumentException("bad filename"); + } return new File(workingDirectory, filename); } if (nonNull(defaultFilename)) { + if (defaultFilename.isBlank() || defaultFilename.contains("..")) { + throw new IllegalArgumentException("bad filename"); + } return new File(workingDirectory, defaultFilename); } - final var path = url.getPath(); + var path = url.getPath(); if (path.isEmpty()) { return createTempFile("download", "file", workingDirectory); + } else if (path.contains("..")) { + throw new IllegalArgumentException("bad filename"); } return new File(workingDirectory, new File(path).getName()); } @@ -188,11 +219,10 @@ private static File getTargetFile(final URL url, * URL or headers, or {@code null} to use a generated name * @return The file downloaded * @throws IOException - * If anything goes wrong. + * If anything goes wrong. */ - public static File downloadFile(final String url, - final File workingDirectory, final String defaultFilename) - throws IOException { + public static File downloadFile(String url, File workingDirectory, + String defaultFilename) throws IOException { return downloadFile(new URL(url), workingDirectory, defaultFilename); } } diff --git a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/utils/Log.java b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/utils/Log.java index 4e599abf70..7b97584d91 100644 --- a/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/utils/Log.java +++ b/SpiNNaker-nmpiexec/src/main/java/uk/ac/manchester/spinnaker/nmpiexec/utils/Log.java @@ -19,7 +19,6 @@ * A very simple-minded logger. */ public abstract class Log { - /** * Avoid instantiation. */ @@ -33,7 +32,7 @@ private Log() { * @param message * The message to write. */ - public static void log(final String message) { + public static void log(String message) { System.err.println(message); } @@ -43,7 +42,7 @@ public static void log(final String message) { * @param exception * The exception to write. */ - public static void log(final Throwable exception) { + public static void log(Throwable exception) { exception.printStackTrace(System.err); } } diff --git a/SpiNNaker-nmpimodel/pom.xml b/SpiNNaker-nmpimodel/pom.xml index b5c261a942..2ce01b5c5b 100644 --- a/SpiNNaker-nmpimodel/pom.xml +++ b/SpiNNaker-nmpimodel/pom.xml @@ -26,16 +26,17 @@ limitations under the License. - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider + com.fasterxml.jackson.jakarta.rs + jackson-jakarta-rs-json-provider com.fasterxml.jackson.datatype jackson-datatype-joda - javax.ws.rs - javax.ws.rs-api + jakarta.ws.rs + jakarta.ws.rs-api + 3.1.0 diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobMachineAllocated.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobMachineAllocated.java index 0bc450b76e..f506011ddd 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobMachineAllocated.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobMachineAllocated.java @@ -35,7 +35,7 @@ public JobMachineAllocated() { * @param allocated * Whether the job was allocated. */ - public JobMachineAllocated(final boolean allocated) { + public JobMachineAllocated(boolean allocated) { this.allocated = allocated; } @@ -54,7 +54,7 @@ public boolean isAllocated() { * @param allocated * The allocation status */ - public void setAllocated(final boolean allocated) { + public void setAllocated(boolean allocated) { this.allocated = allocated; } } diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobManagerInterface.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobManagerInterface.java index e059af1edb..987bbd1734 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobManagerInterface.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobManagerInterface.java @@ -19,17 +19,17 @@ import java.io.InputStream; import java.util.List; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -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.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Response; import uk.ac.manchester.spinnaker.nmpi.model.machine.ChipCoordinates; import uk.ac.manchester.spinnaker.nmpi.model.machine.SpinnakerMachine; diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobSpecification.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobSpecification.java index 23bf3dd8dd..6aa1d1c1bd 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobSpecification.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/JobSpecification.java @@ -54,9 +54,8 @@ public JobSpecification() { * @param url * The URL of the job to send results and status to. */ - public JobSpecification(final SpinnakerMachine machine, - final JobParameters parameters, final int id, - final String url) { + public JobSpecification(SpinnakerMachine machine, JobParameters parameters, + int id, String url) { this.machine = machine; this.parameters = parameters; this.id = id; @@ -78,7 +77,7 @@ public SpinnakerMachine getMachine() { * @param machine * the machine to set */ - public void setMachine(final SpinnakerMachine machine) { + public void setMachine(SpinnakerMachine machine) { this.machine = machine; } @@ -97,7 +96,7 @@ public JobParameters getParameters() { * @param parameters * the parameters to set */ - public void setParameters(final JobParameters parameters) { + public void setParameters(JobParameters parameters) { this.parameters = parameters; } @@ -116,7 +115,7 @@ public int getId() { * @param id * the id to set */ - public void setId(final int id) { + public void setId(int id) { this.id = id; } @@ -135,7 +134,7 @@ public String getUrl() { * @param url * the URL to set */ - public void setUrl(final String url) { + public void setUrl(String url) { this.url = url; } } diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/RemoteStackTrace.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/RemoteStackTrace.java index afce9eceba..f2e8d682ea 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/RemoteStackTrace.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/RemoteStackTrace.java @@ -38,8 +38,8 @@ public RemoteStackTrace() { * @param throwable * The exception to make the stack trace from. */ - public RemoteStackTrace(final Throwable throwable) { - for (final var element : throwable.getStackTrace()) { + public RemoteStackTrace(Throwable throwable) { + for (var element : throwable.getStackTrace()) { elements.add(new RemoteStackTraceElement(element)); } } @@ -50,7 +50,7 @@ public RemoteStackTrace(final Throwable throwable) { * @param elements * The elements to make the stack trace from. */ - public RemoteStackTrace(final List elements) { + public RemoteStackTrace(List elements) { this.elements = elements; } @@ -69,7 +69,7 @@ public List getElements() { * @param elements * The elements to set */ - public void setElements(final List elements) { + public void setElements(List elements) { this.elements = elements; } } diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/RemoteStackTraceElement.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/RemoteStackTraceElement.java index b6c2eda3b5..6dec5a875c 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/RemoteStackTraceElement.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/RemoteStackTraceElement.java @@ -45,7 +45,7 @@ public RemoteStackTraceElement() { * @param element * The stack trace element to convert. */ - public RemoteStackTraceElement(final StackTraceElement element) { + public RemoteStackTraceElement(StackTraceElement element) { this.className = element.getClassName(); this.methodName = element.getMethodName(); this.fileName = element.getFileName(); @@ -67,7 +67,7 @@ public String getClassName() { * @param className * the className to set */ - public void setClassName(final String className) { + public void setClassName(String className) { this.className = className; } @@ -86,7 +86,7 @@ public String getMethodName() { * @param methodName * the methodName to set */ - public void setMethodName(final String methodName) { + public void setMethodName(String methodName) { this.methodName = methodName; } @@ -105,7 +105,7 @@ public String getFileName() { * @param fileName * the fileName to set */ - public void setFileName(final String fileName) { + public void setFileName(String fileName) { this.fileName = fileName; } @@ -124,7 +124,7 @@ public int getLineNumber() { * @param lineNumber * the lineNumber to set */ - public void setLineNumber(final int lineNumber) { + public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/nmpi/DataItem.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/nmpi/DataItem.java index ca82289614..0de5a88005 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/nmpi/DataItem.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/nmpi/DataItem.java @@ -38,7 +38,7 @@ public DataItem() { * @param url * The URL to wrap. */ - public DataItem(final String url) { + public DataItem(String url) { this.url = url; } @@ -57,7 +57,7 @@ public String getUrl() { * @param url * The URL */ - public void setUrl(final String url) { + public void setUrl(String url) { this.url = url; } @@ -71,7 +71,7 @@ public void setUrl(final String url) { * @hidden */ @JsonAnySetter - public void ignoreExtra(final String key, final String value) { + public void ignoreExtra(String key, String value) { // Ignore anything else } } diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/nmpi/Job.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/nmpi/Job.java index 6495c05a40..0b3883b49e 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/nmpi/Job.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/nmpi/Job.java @@ -63,7 +63,7 @@ public String getCode() { * @param code * the code to set */ - public void setCode(final String code) { + public void setCode(String code) { this.code = code; } @@ -82,8 +82,7 @@ public Map getHardwareConfig() { * @param hardwareConfig * the hardwareConfig to set */ - public void setHardwareConfig( - final Map hardwareConfig) { + public void setHardwareConfig(Map hardwareConfig) { this.hardwareConfig = hardwareConfig; } @@ -102,7 +101,7 @@ public String getHardwarePlatform() { * @param hardwarePlatform * the hardwarePlatform to set */ - public void setHardwarePlatform(final String hardwarePlatform) { + public void setHardwarePlatform(String hardwarePlatform) { this.hardwarePlatform = hardwarePlatform; } @@ -121,7 +120,7 @@ public Integer getId() { * @param id * the id to set */ - public void setId(final Integer id) { + public void setId(Integer id) { this.id = id; } @@ -140,7 +139,7 @@ public List getInputData() { * @param inputData * the inputData to set */ - public void setInputData(final List inputData) { + public void setInputData(List inputData) { this.inputData = inputData; } @@ -159,7 +158,7 @@ public String getCollab() { * @param collab * the collab to set */ - public void setCollab(final String collab) { + public void setCollab(String collab) { this.collab = collab; } @@ -178,7 +177,7 @@ public String getCommand() { * @param command * the command to set */ - public void setCommand(final String command) { + public void setCommand(String command) { this.command = command; } @@ -197,7 +196,7 @@ public String getUserId() { * @param userId * the userId to set */ - public void setUserId(final String userId) { + public void setUserId(String userId) { this.userId = userId; } @@ -211,7 +210,7 @@ public void setUserId(final String userId) { * @hidden */ @JsonAnySetter - public void set(final String name, final Object value) { + public void set(String name, Object value) { // Ignore } } diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/pynn/PyNNJobParameters.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/pynn/PyNNJobParameters.java index b661ff9214..113f8740e8 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/pynn/PyNNJobParameters.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/job/pynn/PyNNJobParameters.java @@ -56,9 +56,8 @@ public PyNNJobParameters() { * @param hardwareConfiguration * The hardware configuration desired. */ - public PyNNJobParameters(final String workingDirectory, - final String setupScript, final String userScript, - final Map hardwareConfiguration) { + public PyNNJobParameters(String workingDirectory, String setupScript, + String userScript, Map hardwareConfiguration) { this.workingDirectory = workingDirectory; this.userScript = userScript; this.setupScript = setupScript; @@ -80,7 +79,7 @@ public String getWorkingDirectory() { * @param workingDirectory * the workingDirectory to set */ - public void setWorkingDirectory(final String workingDirectory) { + public void setWorkingDirectory(String workingDirectory) { this.workingDirectory = workingDirectory; } @@ -99,7 +98,7 @@ public String getSetupScript() { * @param setupScript * the script */ - public void setSetupScript(final String setupScript) { + public void setSetupScript(String setupScript) { this.setupScript = setupScript; } @@ -118,7 +117,7 @@ public String getUserScript() { * @param userScript * the script to set */ - public void setUserScript(final String userScript) { + public void setUserScript(String userScript) { this.userScript = userScript; } @@ -138,7 +137,7 @@ public Map getHardwareConfiguration() { * the hardwareConfiguration to set */ public void setHardwareConfiguration( - final Map hardwareConfiguration) { + Map hardwareConfiguration) { this.hardwareConfiguration = hardwareConfiguration; } } diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/machine/ChipCoordinates.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/machine/ChipCoordinates.java index 3b6b5a7975..692ab7e3b3 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/machine/ChipCoordinates.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/machine/ChipCoordinates.java @@ -42,8 +42,7 @@ public class ChipCoordinates { * @param board * The board containing the chip. */ - public ChipCoordinates(final int cabinet, final int frame, - final int board) { + public ChipCoordinates(int cabinet, int frame, int board) { this.cabinet = cabinet; this.frame = frame; this.board = board; diff --git a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/machine/SpinnakerMachine.java b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/machine/SpinnakerMachine.java index f927c13f40..e7ee540b84 100644 --- a/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/machine/SpinnakerMachine.java +++ b/SpiNNaker-nmpimodel/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/machine/SpinnakerMachine.java @@ -20,7 +20,9 @@ import static java.util.Comparator.nullsFirst; import static java.util.Objects.nonNull; +import java.io.Serial; import java.io.Serializable; +import java.util.Arrays; import java.util.Comparator; import java.util.Objects; @@ -29,75 +31,47 @@ */ public class SpinnakerMachine implements Serializable, Comparable { - - /** - * Serial version UID. - */ + /** Serial version UID. */ + @Serial private static final long serialVersionUID = -2247744763327978524L; - /** - * The number of parts that make up a machine description as a string. - */ + /** The number of parts that make up a machine description as a string. */ private static final int N_PARTS = 6; - /** - * Part of the string that is the name of the machine. - */ + /** Part of the string that is the name of the machine. */ private static final int MACHINE_NAME_PART = 0; - /** - * Part of the string that is the version of the machine. - */ + /** Part of the string that is the version of the machine. */ private static final int VERSION_PART = 1; - /** - * Part of the string that is the width of the machine. - */ + /** Part of the string that is the width of the machine. */ private static final int WIDTH_PART = 2; - /** - * Part of the string that is the height of the machine. - */ + /** Part of the string that is the height of the machine. */ private static final int HEIGHT_PART = 3; - /** - * Part of the string that is the number of boards in the machine. - */ + /** Part of the string that is the number of boards in the machine. */ private static final int N_BOARDS_PART = 4; - /** - * Part of the string that is the BMP details of the machine. - */ + /** Part of the string that is the BMP details of the machine. */ private static final int BMP_DETAILS_PART = 5; - /** - * The name of the machine. - */ + /** The name of the machine. */ private String machineName = null; - /** - * The version of the machine. - */ + /** The version of the machine. */ private String version = null; - /** - * The width of the machine. - */ + /** The width of the machine. */ private int width = 0; - /** - * The height of the machine. - */ + /** The height of the machine. */ private int height = 0; - /** - * The number of boards in the machine. - */ + /** The number of boards in the machine. */ private int nBoards = 0; - /** - * The BMP details of the machine. - */ + /** The BMP details of the machine. */ private String bmpDetails = null; /** @@ -118,13 +92,13 @@ public SpinnakerMachine() { * @throws NumberFormatException * if one of the parts that should be numeric isn't */ - public static SpinnakerMachine parse(final String value) { + public static SpinnakerMachine parse(String value) { if (!value.startsWith("(") || !value.endsWith(")")) { throw new IllegalArgumentException("Cannot convert string \"" + value + "\" - missing start and end brackets"); } - final var parts = value.substring(1, value.length() - 1).split(":"); + var parts = value.substring(1, value.length() - 1).split(":"); if (parts.length != N_PARTS) { throw new IllegalArgumentException( "Wrong number of :-separated arguments - " + parts.length @@ -144,13 +118,12 @@ public static SpinnakerMachine parse(final String value) { */ @Override public String toString() { - final var output = new StringBuilder(); + var output = new StringBuilder(); // Note: List.of won't work here because things can be null and // List.of doesn't allow null things - final var potentials = new Object[] { - machineName, version, bmpDetails, width, height, bmpDetails - }; - for (final var potential : potentials) { + var potentials = Arrays.asList(machineName, version, bmpDetails, width, + height, bmpDetails); + for (var potential : potentials) { if (nonNull(potential)) { if (output.length() > 0) { output.append(':'); @@ -177,9 +150,8 @@ public String toString() { * @param bmpDetails * How to contact the machine's Board Management Processor */ - public SpinnakerMachine(final String machineName, final String version, - final int width, final int height, final int numBoards, - final String bmpDetails) { + public SpinnakerMachine(String machineName, String version, int width, + int height, int numBoards, String bmpDetails) { this.machineName = machineName; this.version = version; this.width = width; @@ -203,7 +175,7 @@ public String getMachineName() { * @param machineName * The name of the machine */ - public void setMachineName(final String machineName) { + public void setMachineName(String machineName) { this.machineName = machineName; } @@ -222,7 +194,7 @@ public String getVersion() { * @param version * The version of the machine */ - public void setVersion(final String version) { + public void setVersion(String version) { this.version = version; } @@ -241,7 +213,7 @@ public int getWidth() { * @param width * The width of the machine */ - public void setWidth(final int width) { + public void setWidth(int width) { this.width = width; } @@ -260,7 +232,7 @@ public int getHeight() { * @param height * The height of the machine */ - public void setHeight(final int height) { + public void setHeight(int height) { this.height = height; } @@ -284,7 +256,7 @@ public int getnBoards() { * @param nBoards * The number of boards in the machine */ - public void setnBoards(final int nBoards) { + public void setnBoards(int nBoards) { this.nBoards = nBoards; } @@ -303,7 +275,7 @@ public String getBmpDetails() { * @param bmpDetails * The BMP details of the machine */ - public void setBmpDetails(final String bmpDetails) { + public void setBmpDetails(String bmpDetails) { this.bmpDetails = bmpDetails; } @@ -311,15 +283,11 @@ public void setBmpDetails(final String bmpDetails) { * Check for equality with another machine. */ @Override - public boolean equals(final Object o) { - if (o instanceof SpinnakerMachine) { - // TODO Is this the right way to determine equality? - final var m = (SpinnakerMachine) o; - return Objects.equals(machineName, m.machineName) - && Objects.equals(version, m.version); - } else { - return false; - } + public boolean equals(Object o) { + // TODO Is this the right way to determine equality? + return (o instanceof SpinnakerMachine m) + && Objects.equals(machineName, m.machineName) + && Objects.equals(version, m.version); } /** Null-safe string comparator. */ @@ -336,7 +304,7 @@ public boolean equals(final Object o) { * Compare to another machine; order by name then by version. */ @Override - public int compareTo(final SpinnakerMachine m) { + public int compareTo(SpinnakerMachine m) { return M_COMPARE.compare(this, m); } diff --git a/SpiNNaker-nmpiserv/pom.xml b/SpiNNaker-nmpiserv/pom.xml index 08ae64300f..80faa16ead 100644 --- a/SpiNNaker-nmpiserv/pom.xml +++ b/SpiNNaker-nmpiserv/pom.xml @@ -247,6 +247,10 @@ limitations under the License. + + ${project.groupId} + SpiNNaker-machine + ${project.groupId} SpiNNaker-nmpiexec @@ -310,8 +314,8 @@ limitations under the License. - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider + com.fasterxml.jackson.jakarta.rs + jackson-jakarta-rs-json-provider org.springframework.boot @@ -324,11 +328,6 @@ limitations under the License. - - javax - javaee-api - provided - org.slf4j slf4j-api @@ -379,7 +378,6 @@ limitations under the License. - devtools diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/DockerExecutorFactory.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/DockerExecutorFactory.java index 790a8e15c9..d91e08314b 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/DockerExecutorFactory.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/DockerExecutorFactory.java @@ -26,12 +26,14 @@ import java.util.ArrayList; import java.util.List; -import javax.annotation.PostConstruct; -import javax.ws.rs.NotFoundException; +import jakarta.annotation.PostConstruct; +import jakarta.ws.rs.NotFoundException; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; +import com.google.errorprone.annotations.concurrent.GuardedBy; + import uk.ac.manchester.spinnaker.nmpi.rest.DockerAPI; import uk.ac.manchester.spinnaker.nmpi.rest.DockerCreateRequest; import uk.ac.manchester.spinnaker.nmpi.rest.DockerInspectResponse; @@ -39,7 +41,7 @@ /** * Executor factory that uses Docker to run jobs. */ -public class DockerExecutorFactory implements JobExecuterFactory { +public final class DockerExecutorFactory implements JobExecuterFactory { /** Time to wait between docker inspects while waiting for finish. */ private static final int WAIT_SLEEP_TIME_MS = 1000; @@ -57,7 +59,7 @@ public class DockerExecutorFactory implements JobExecuterFactory { @Value("${liveUploadOutput}") private boolean liveUploadOutput; - /** True if a spinnaker machine should be requested. */ + /** True if a SpiNNaker machine should be requested. */ @Value("${requestSpiNNakerMachine}") private boolean requestSpiNNakerMachine; @@ -69,6 +71,7 @@ public class DockerExecutorFactory implements JobExecuterFactory { private int maxNVirtualMachines; /** The current number of VMs. */ + @GuardedBy("lock") private int nVirtualMachines = 0; /** The docker client. */ @@ -96,8 +99,8 @@ private void init() { } @Override - public JobExecuter createJobExecuter(final JobManager manager, - final URL baseUrl) throws IOException { + public JobExecuter createJobExecuter(JobManager manager, URL baseUrl) + throws IOException { requireNonNull(manager); requireNonNull(baseUrl); waitToClaimVM(); @@ -143,7 +146,7 @@ protected final class Executor implements JobExecuter { private String id; - private Executor(final JobManager jobManager, final URL baseUrl) + private Executor(JobManager jobManager, URL baseUrl) throws IOException { this.manager = jobManager; uuid = randomUUID().toString(); @@ -175,7 +178,8 @@ public void startExecuter() { var response = dockerApi.create( new DockerCreateRequest(image, args)); id = response.getId(); - logger.info("Created docker container {}, warnings: {}", id); + logger.info("Created docker container {}, warnings: {}", id, + response.getWarnings()); dockerApi.start(id); new Thread(threadGroup, this::waitForExit, "Docker Executer (" + uuid + ")").start(); @@ -195,7 +199,7 @@ private void waitForRunning(boolean running) { } catch (InterruptedException e) { return; } - } while (res.getState().isRunning() != running); + } while (res == null || res.getState().isRunning() != running); } /** @@ -221,5 +225,4 @@ protected void waitForExit() { } } } - } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/JobExecuterFactory.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/JobExecuterFactory.java index 060fdb9b06..df43c8f62e 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/JobExecuterFactory.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/JobExecuterFactory.java @@ -21,10 +21,12 @@ /** * A factory for creating job executers. * + * @see DockerExecutorFactory * @see LocalJobExecuterFactory * @see XenVMExecuterFactory */ -public interface JobExecuterFactory { +public sealed interface JobExecuterFactory permits DockerExecutorFactory, + LocalJobExecuterFactory, XenVMExecuterFactory { /** * Creates a new {@link JobExecuter}. * diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/JobManager.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/JobManager.java index f7a222094d..5c0a4e6df1 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/JobManager.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/JobManager.java @@ -23,11 +23,12 @@ import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.concurrent.Executors.newScheduledThreadPool; +import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; -import static javax.ws.rs.core.Response.ok; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; -import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; -import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static jakarta.ws.rs.core.Response.ok; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; import static org.apache.commons.io.FileUtils.copyInputStreamToFile; import static org.apache.commons.io.FileUtils.forceDelete; import static org.apache.commons.io.FileUtils.forceMkdir; @@ -46,13 +47,12 @@ import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.ws.rs.InternalServerErrorException; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.ws.rs.InternalServerErrorException; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -171,7 +171,7 @@ public class JobManager implements NMPIQueueListener, JobManagerInterface { * @param baseUrl * The URL of the REST service of the manager. */ - public JobManager(final URL baseUrl) { + public JobManager(URL baseUrl) { this.baseUrl = requireNonNull(baseUrl); logger.info("Base URL is {}", baseUrl); } @@ -187,8 +187,7 @@ private void startManager() throws IOException { threadGroup = new ThreadGroup("NMPI"); // Start looking for jobs after the startup of the services - scheduler.schedule(() -> startJobs(), STATUS_UPDATE_PERIOD, - TimeUnit.SECONDS); + scheduler.schedule(this::startJobs, STATUS_UPDATE_PERIOD, SECONDS); } private void startJobs() { @@ -196,7 +195,7 @@ private void startJobs() { // again for (var job : queueManager.getJobs()) { try { - addJob((Job) job); + addJob(job); } catch (IOException e) { logger.error("Error adding job at startup", e); } @@ -207,7 +206,7 @@ private void startJobs() { new Thread(threadGroup, queueManager::processResponsesFromQueue, "QueueManager").start(); scheduler.scheduleAtFixedRate(this::updateStatus, - 0, STATUS_UPDATE_PERIOD, TimeUnit.SECONDS); + 0, STATUS_UPDATE_PERIOD, SECONDS); } /** @@ -220,7 +219,7 @@ private void stopManager() { } @Override - public void addJob(final Job job) throws IOException { + public void addJob(Job job) throws IOException { requireNonNull(job); logger.info("New job {}", job.getId()); jobOwner.put(job.getId(), job.getUserId()); @@ -238,9 +237,8 @@ public void addJob(final Job job) throws IOException { * @throws IOException * If there is an error starting the job */ - private void launchExecuter(final Job job) throws IOException { - final var executer = - jobExecuterFactory.createJobExecuter(this, baseUrl); + private void launchExecuter(Job job) throws IOException { + var executer = jobExecuterFactory.createJobExecuter(this, baseUrl); synchronized (jobExecuters) { var executerId = executer.getExecuterId(); jobExecuters.put(executerId, executer); @@ -251,7 +249,7 @@ private void launchExecuter(final Job job) throws IOException { } @Override - public Job getNextJob(final String executerId) { + public Job getNextJob(String executerId) { requireNonNull(executerId); Job job = null; synchronized (jobExecuters) { @@ -267,8 +265,7 @@ public Job getNextJob(final String executerId) { } @Override - public SpinnakerMachine getLargestJobMachine(final int id, - final double runTime) { + public SpinnakerMachine getLargestJobMachine(int id, double runTime) { // TODO Check quota to get the largest machine within the quota try { return machineManager.getMachines().stream() @@ -280,9 +277,8 @@ public SpinnakerMachine getLargestJobMachine(final int id, } @Override - public SpinnakerMachine getJobMachine(final int id, final int nCores, - final int nChips, final int nBoards, final double runTime) { - + public SpinnakerMachine getJobMachine(int id, int nCores, int nChips, + int nBoards, double runTime) { logger.info( "Request for {} cores or {} chips or {} boards for {} seconds", nCores, nChips, nBoards, runTime / MILLISECONDS_PER_SECOND); @@ -315,7 +311,7 @@ public SpinnakerMachine getJobMachine(final int id, final int nCores, nBoardsToRequest = (int) nBoardsExact; } - final var machine = allocateMachineForJob(id, jobOwner.get(id), + var machine = allocateMachineForJob(id, jobOwner.get(id), nBoardsToRequest); logger.info("Running {} on {}", id, machine.getMachineName()); addProvenance(id, asList("spinnaker_machine"), @@ -337,8 +333,8 @@ public SpinnakerMachine getJobMachine(final int id, final int nCores, * @throws WebApplicationException * if machine not found */ - private SpinnakerMachine findMachine(final int id, - final String machineName, final boolean remove) { + private SpinnakerMachine findMachine(int id, + String machineName, boolean remove) { var machines = allocatedMachines.get(id); if (isNull(machines)) { throw new WebApplicationException( @@ -358,7 +354,7 @@ private SpinnakerMachine findMachine(final int id, } @Override - public void releaseMachine(final int id, final String machineName) { + public void releaseMachine(int id, String machineName) { synchronized (allocatedMachines) { var machine = findMachine(id, machineName, true); try { @@ -370,8 +366,7 @@ public void releaseMachine(final int id, final String machineName) { } @Override - public void setMachinePower(final int id, final String machineName, - final boolean powerOn) { + public void setMachinePower(int id, String machineName, boolean powerOn) { synchronized (allocatedMachines) { var machine = findMachine(id, machineName, false); try { @@ -383,8 +378,8 @@ public void setMachinePower(final int id, final String machineName, } @Override - public ChipCoordinates getChipCoordinates(final int id, - final String machineName, final int chipX, final int chipY) { + public ChipCoordinates getChipCoordinates(int id, String machineName, + int chipX, int chipY) { synchronized (allocatedMachines) { var machine = findMachine(id, machineName, false); try { @@ -406,10 +401,10 @@ public ChipCoordinates getChipCoordinates(final int id, * The number of boards to request * @return The machine allocated */ - private SpinnakerMachine allocateMachineForJob(final int id, - final String user, final int nBoardsToRequest) { + private SpinnakerMachine allocateMachineForJob(int id, String user, + int nBoardsToRequest) { try { - final var machine = machineManager.getNextAvailableMachine( + var machine = machineManager.getNextAvailableMachine( nBoardsToRequest, user, id); synchronized (allocatedMachines) { allocatedMachines.computeIfAbsent( @@ -428,18 +423,18 @@ private SpinnakerMachine allocateMachineForJob(final int id, * The id of the job. * @return The list of machines for the job. */ - private List getMachineForJob(final int id) { + private List getMachineForJob(int id) { synchronized (allocatedMachines) { return allocatedMachines.get(id); } } @Override - public void extendJobMachineLease(final int id, final double runTime) { + public void extendJobMachineLease(int id, double runTime) { // Does Nothing } - private boolean isMachineAvailable(final SpinnakerMachine machine) { + private boolean isMachineAvailable(SpinnakerMachine machine) { try { return machineManager.isMachineAvailable(machine); } catch (IOException e) { @@ -448,9 +443,8 @@ private boolean isMachineAvailable(final SpinnakerMachine machine) { } @Override - public JobMachineAllocated checkMachineLease(final int id, - final int waitTime) { - final var machines = getMachineForJob(id); + public JobMachineAllocated checkMachineLease(int id, int waitTime) { + var machines = getMachineForJob(id); // Return false if any machine is gone if (!machines.stream().allMatch(this::isMachineAvailable)) { @@ -473,12 +467,12 @@ public JobMachineAllocated checkMachineLease(final int id, * @param machines * What to wait for events from. */ - private void waitForAnyMachineStateChange(final int waitTime, - final List machines) { - final var stateChangeSync = new LinkedBlockingQueue<>(); + private void waitForAnyMachineStateChange(int waitTime, + List machines) { + var stateChangeSync = new LinkedBlockingQueue<>(); try { - for (final var machine : machines) { - final var stateThread = new Thread(threadGroup, () -> { + for (var machine : machines) { + var stateThread = new Thread(threadGroup, () -> { try { machineManager.waitForMachineStateChange(machine, waitTime); @@ -491,41 +485,41 @@ private void waitForAnyMachineStateChange(final int waitTime, stateThread.start(); } stateChangeSync.take(); - } catch (final InterruptedException e) { + } catch (InterruptedException e) { // Does Nothing } } @Override - public void appendLog(final int id, final String logToAppend) { + public void appendLog(int id, String logToAppend) { logger.debug("Updating log for {}", id); logger.trace("{}: {}", id, logToAppend); queueManager.appendJobLog(id, requireNonNull(logToAppend)); } @Override - public void addOutput(final String projectId, final int id, - final String output, final InputStream input) { + public void addOutput(String projectId, int id, String output, + InputStream input) { requireNonNull(output); requireNonNull(input); try { if (!jobOutputTempFiles.containsKey(id)) { - final var tempOutputDir = createTempFile("jobOutput", ".tmp"); + var tempOutputDir = createTempFile("jobOutput", ".tmp"); forceDelete(tempOutputDir); forceMkdir(tempOutputDir); jobOutputTempFiles.put(id, tempOutputDir); } - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error creating temporary output directory for {}", id, e); throw new WebApplicationException(INTERNAL_SERVER_ERROR); } - final var outputFile = new File(jobOutputTempFiles.get(id), output); + var outputFile = new File(jobOutputTempFiles.get(id), output); try { forceMkdirParent(outputFile); copyInputStreamToFile(input, outputFile); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error writing file {} for job {}", outputFile, id, e); throw new WebApplicationException(INTERNAL_SERVER_ERROR); @@ -547,17 +541,15 @@ public void addOutput(final String projectId, final int id, * @throws IOException * If there was an error dealing with a file. */ - private List getOutputFiles(final String projectId, final int id, - final String baseFile, final List outputs) - throws IOException { - final var outputItems = new ArrayList(); + private List getOutputFiles(String projectId, int id, + String baseFile, List outputs) throws IOException { + var outputItems = new ArrayList(); if (nonNull(outputs)) { - final var outputFiles = - outputs.stream().map(File::new).collect(toList()); + var outputFiles = outputs.stream().map(File::new).collect(toList()); outputItems.addAll(outputManager.addOutputs(projectId, id, new File(baseFile), outputFiles)); } - final var directory = jobOutputTempFiles.remove(id); + var directory = jobOutputTempFiles.remove(id); if (nonNull(directory)) { outputItems.addAll(outputManager.addOutputs(projectId, id, directory, listFiles(directory, null, true))); @@ -566,10 +558,9 @@ private List getOutputFiles(final String projectId, final int id, } @Override - public void addProvenance(final int id, final List path, - final String value) { + public void addProvenance(int id, List path, String value) { synchronized (jobProvenance) { - final var provenance = jobProvenance.computeIfAbsent(id, + var provenance = jobProvenance.computeIfAbsent(id, ignored -> new ObjectNode(JsonNodeFactory.instance)); // Traverse the object node to find the path to add to @@ -585,8 +576,8 @@ public void addProvenance(final int id, final List path, } // If the item is an ObjectNode, go to the next item - if (subNode instanceof ObjectNode) { - current = (ObjectNode) subNode; + if (subNode instanceof ObjectNode sn) { + current = sn; // If the item exists and is not an ObjectNode, this is an // error as a non-object can't contain values @@ -613,16 +604,15 @@ public void addProvenance(final int id, final List path, * The ID of the job * @return The provenance as a JSON data item */ - private ObjectNode getProvenance(final int id) { + private ObjectNode getProvenance(int id) { synchronized (jobProvenance) { return jobProvenance.remove(id); } } @Override - public void setJobFinished(final String projectId, final int id, - final String logToAppend, final String baseDirectory, - final List outputs) { + public void setJobFinished(String projectId, int id, String logToAppend, + String baseDirectory, List outputs) { requireNonNull(projectId); requireNonNull(logToAppend); requireNonNull(baseDirectory); @@ -632,18 +622,18 @@ public void setJobFinished(final String projectId, final int id, releaseAllocatedMachines(id); // Do these before anything that can throw - final var prov = getProvenance(id); + var prov = getProvenance(id); try { queueManager.setJobFinished(id, logToAppend, getOutputFiles(projectId, id, baseDirectory, outputs), prov); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error creating URLs while updating job", e); } } - private void releaseMachine(final SpinnakerMachine machine) { + private void releaseMachine(SpinnakerMachine machine) { try { machineManager.releaseMachine(machine); } catch (IOException e) { @@ -658,9 +648,9 @@ private void releaseMachine(final SpinnakerMachine machine) { * The ID of the job * @return {@code true} if there were machines removed by this. */ - private boolean releaseAllocatedMachines(final int id) { + private boolean releaseAllocatedMachines(int id) { synchronized (allocatedMachines) { - final var machines = allocatedMachines.remove(id); + var machines = allocatedMachines.remove(id); if (nonNull(machines)) { machines.forEach(this::releaseMachine); } @@ -669,10 +659,9 @@ private boolean releaseAllocatedMachines(final int id) { } @Override - public void setJobError(final String projectId, final int id, - final String error, final String logToAppend, - final String baseDirectory, final List outputs, - final RemoteStackTrace stackTrace) { + public void setJobError(String projectId, int id, String error, + String logToAppend, String baseDirectory, List outputs, + RemoteStackTrace stackTrace) { requireNonNull(projectId); requireNonNull(error); requireNonNull(logToAppend); @@ -685,15 +674,15 @@ public void setJobError(final String projectId, final int id, logger.info("Marking job {} as error", id); releaseAllocatedMachines(id); - final var exception = reconstructRemoteException(error, stackTrace); + var exception = reconstructRemoteException(error, stackTrace); // Do these before anything that can throw - final var prov = getProvenance(id); + var prov = getProvenance(id); try { queueManager.setJobError(id, logToAppend, getOutputFiles(projectId, id, baseDirectory, outputs), exception, prov); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error creating URLs while updating job", e); } } @@ -707,9 +696,9 @@ public void setJobError(final String projectId, final int id, * The stack trace. * @return The exception. */ - private Exception reconstructRemoteException(final String error, - final RemoteStackTrace stackTrace) { - final var exception = new Exception(error); + private Exception reconstructRemoteException(String error, + RemoteStackTrace stackTrace) { + var exception = new Exception(error); exception.setStackTrace(stackTrace.getElements().stream() .map(RemoteStackTraceElement::toSTE) .toArray(StackTraceElement[]::new)); @@ -724,27 +713,26 @@ private Exception reconstructRemoteException(final String error, * @param logToAppend * The log messages */ - public void setExecutorExited(final String executorId, - final String logToAppend) { + public void setExecutorExited(String executorId, String logToAppend) { Job job = null; synchronized (jobExecuters) { job = executorJobId.remove(requireNonNull(executorId)); jobExecuters.remove(executorId); } if (nonNull(job)) { - final int id = job.getId(); + int id = job.getId(); if (jobOwner.containsKey(id)) { logger.debug("Executer {} for Job {} has exited, " + "but job not exited cleanly", executorId, id); jobOwner.remove(id); releaseAllocatedMachines(id); - final var prov = getProvenance(id); + var prov = getProvenance(id); try { - final var projectId = job.getCollab(); + var projectId = job.getCollab(); queueManager.setJobError(id, logToAppend, getOutputFiles(projectId, id, null, null), new Exception("Job did not finish cleanly"), prov); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error creating URLs while updating job", e); queueManager.setJobError(id, logToAppend, new ArrayList(), @@ -767,7 +755,7 @@ public void setExecutorExited(final String executorId, @Override public Response getJobProcessManager() { - final var jobManagerStream = + var jobManagerStream = getClass().getResourceAsStream("/" + JOB_PROCESS_MANAGER); if (isNull(jobManagerStream)) { throw new UnsatisfiedLinkError( diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/LocalJobExecuterFactory.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/LocalJobExecuterFactory.java index a0e67a6302..b1dcc1492b 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/LocalJobExecuterFactory.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/LocalJobExecuterFactory.java @@ -42,7 +42,7 @@ import java.util.List; import java.util.UUID; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; @@ -50,7 +50,7 @@ /** * An executer that runs its subprocesses on the local machine. */ -public class LocalJobExecuterFactory implements JobExecuterFactory { +public final class LocalJobExecuterFactory implements JobExecuterFactory { /** * Get the java executable. * @@ -59,7 +59,7 @@ public class LocalJobExecuterFactory implements JobExecuterFactory { * If the file can't be instantiated */ private static File getJavaExec() throws IOException { - final var binDir = new File(System.getProperty("java.home"), "bin"); + var binDir = new File(System.getProperty("java.home"), "bin"); var exec = new File(binDir, "java"); if (!exec.canExecute()) { exec = new File(binDir, "java.exe"); @@ -104,30 +104,32 @@ public LocalJobExecuterFactory() { @PostConstruct private void installJobExecuter() throws IOException { // Find the JobManager resource - final var jobManagerStream = + var jobManagerStream = getClass().getResourceAsStream("/" + JOB_PROCESS_MANAGER); if (isNull(jobManagerStream)) { throw new UnsatisfiedLinkError( "/" + JOB_PROCESS_MANAGER + " not found in classpath"); } - // Create a temporary folder - jobExecuterDirectory = createTempFile("jobExecuter", "tmp"); - jobExecuterDirectory.delete(); - jobExecuterDirectory.mkdirs(); - jobExecuterDirectory.deleteOnExit(); - - // Extract the JobManager resources - forceMkdir(jobExecuterDirectory); - copyToFile(jobManagerStream, new File(jobExecuterDirectory, - JOB_PROCESS_MANAGER)); + try (jobManagerStream) { + // Create a temporary folder + jobExecuterDirectory = createTempFile("jobExecuter", "tmp"); + jobExecuterDirectory.delete(); + jobExecuterDirectory.mkdirs(); + jobExecuterDirectory.deleteOnExit(); + + // Extract the JobManager resources + forceMkdir(jobExecuterDirectory); + copyToFile(jobManagerStream, + new File(jobExecuterDirectory, JOB_PROCESS_MANAGER)); + } } @Override - public JobExecuter createJobExecuter(final JobManager manager, - final URL baseUrl) throws IOException { - final var uuid = UUID.randomUUID().toString(); - final var arguments = new ArrayList(); + public JobExecuter createJobExecuter(JobManager manager, URL baseUrl) + throws IOException { + var uuid = UUID.randomUUID().toString(); + var arguments = new ArrayList(); arguments.add("--serverUrl"); arguments.add(requireNonNull(baseUrl).toString()); arguments.add("--local"); @@ -149,7 +151,7 @@ public JobExecuter createJobExecuter(final JobManager manager, /** * The executer thread. */ - protected class Executer implements JobExecuter { + protected final class Executer implements JobExecuter { /** The job manager to report to. */ private final JobManager jobManager; @@ -183,8 +185,8 @@ protected class Executer implements JobExecuter { * @throws IOException * If there is an error creating the log file */ - Executer(final JobManager jobManager, final List arguments, - final String id) throws IOException { + Executer(JobManager jobManager, List arguments, String id) + throws IOException { this.jobManager = jobManager; this.arguments = arguments; this.id = id; @@ -207,7 +209,7 @@ private void runSubprocess() { logger.debug("Waiting for process to finish"); try { process.waitFor(); - } catch (final InterruptedException e) { + } catch (InterruptedException e) { // Do nothing; the thread will terminate shortly } logger.debug("Process finished, closing pipe"); @@ -222,11 +224,11 @@ private void runSubprocess() { * @return The arguments as a list of strings. */ private List constructArguments() { - final var command = new ArrayList(); + var command = new ArrayList(); command.add(javaExec.getAbsolutePath()); command.add("-jar"); command.add(JOB_PROCESS_MANAGER); - for (final var argument : arguments) { + for (var argument : arguments) { command.add(argument); logger.debug("Argument: {}", argument); } @@ -240,8 +242,8 @@ private List constructArguments() { * The command and arguments * @return The output of the process as a pipe */ - private JobOutputPipe startSubprocess(final List command) { - final var builder = new ProcessBuilder(command); + private JobOutputPipe startSubprocess(List command) { + var builder = new ProcessBuilder(command); builder.directory(jobExecuterDirectory); logger.debug("Working directory: {}", jobExecuterDirectory); builder.redirectErrorStream(true); @@ -250,12 +252,11 @@ private JobOutputPipe startSubprocess(final List command) { logger.debug("Starting execution process"); process = builder.start(); logger.debug("Starting pipe from process"); - var pipe = new JobOutputPipe( - process.getInputStream(), + var pipe = new JobOutputPipe(process.getInputStream(), new PrintWriter(outputLog)); pipe.start(); return pipe; - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error running external job", e); startException = e; return null; @@ -272,7 +273,7 @@ private void reportResult() { var loggedOutput = new StringWriter(); try (var reader = new FileReader(outputLog)) { copy(reader, loggedOutput); - } catch (final IOException e) { + } catch (IOException e) { logger.warn("problem in reporting log", e); } jobManager.setExecutorExited(id, loggedOutput.toString()); @@ -329,7 +330,7 @@ class JobOutputPipe extends Thread implements AutoCloseable { * Where things are going to. This class will close this when * it is no longer required. */ - JobOutputPipe(final InputStream input, final PrintWriter output) { + JobOutputPipe(InputStream input, PrintWriter output) { super(threadGroup, "JobOutputPipe"); reader = buffer(new InputStreamReader(input)); writer = output; @@ -340,7 +341,7 @@ class JobOutputPipe extends Thread implements AutoCloseable { private String readLine() { try { return reader.readLine(); - } catch (final IOException e) { + } catch (IOException e) { return null; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/OutputManagerImpl.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/OutputManagerImpl.java index 83a822c2cf..18b3722ad2 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/OutputManagerImpl.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/OutputManagerImpl.java @@ -23,11 +23,11 @@ import static java.util.concurrent.Executors.newScheduledThreadPool; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static javax.ws.rs.core.Response.ok; -import static javax.ws.rs.core.Response.serverError; -import static javax.ws.rs.core.Response.status; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; -import static javax.ws.rs.core.Response.Status.NOT_FOUND; +import static jakarta.ws.rs.core.Response.ok; +import static jakarta.ws.rs.core.Response.serverError; +import static jakarta.ws.rs.core.Response.status; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.utils.ThreadUtils.waitfor; @@ -43,12 +43,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ScheduledExecutorService; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; @@ -132,7 +133,7 @@ private class JobLock implements AutoCloseable { * @param dir * The directory to lock */ - JobLock(final File dir) { + JobLock(File dir) { this.dir = dir; LockToken lock; @@ -151,7 +152,7 @@ private class JobLock implements AutoCloseable { @Override public void close() { synchronized (synchronizers) { - final var lock = synchronizers.get(dir); + var lock = synchronizers.get(dir); if (!lock.unlock()) { synchronizers.remove(dir); } @@ -166,7 +167,7 @@ public void close() { * The base URL of the overall service, used when generating * internal URLs. */ - public OutputManagerImpl(final URL baseServerUrl) { + public OutputManagerImpl(URL baseServerUrl) { this.baseServerUrl = baseServerUrl; } @@ -177,7 +178,7 @@ public OutputManagerImpl(final URL baseServerUrl) { * The number of days to keep the results */ @Value("${results.purge.days}") - void setPurgeTimeout(final long nDaysToKeepResults) { + void setPurgeTimeout(long nDaysToKeepResults) { timeToKeepResults = MILLISECONDS.convert(nDaysToKeepResults, DAYS); } @@ -208,12 +209,12 @@ private void stopPurgeScheduler() { * The id of the project * @return The directory of the project */ - private File getProjectDirectory(final String projectId) { + private File getProjectDirectory(String projectId) { if (isNull(projectId) || projectId.isEmpty() || projectId.endsWith("/")) { throw new IllegalArgumentException("bad projectId"); } - final var name = new File(projectId).getName(); + var name = new File(projectId).getName(); if (name.equals(".") || name.equals("..") || name.isEmpty()) { throw new IllegalArgumentException("bad projectId"); } @@ -221,21 +222,20 @@ private File getProjectDirectory(final String projectId) { } @Override - public List addOutputs(final String projectId, final int id, - final File baseDirectory, final Collection outputs) - throws IOException { + public List addOutputs(String projectId, int id, + File baseDirectory, Collection outputs) throws IOException { if (isNull(outputs)) { return null; } - final var pId = new File(projectId).getName(); - final int pathStart = baseDirectory.getAbsolutePath().length(); - final var projectDirectory = getProjectDirectory(projectId); - final var idDirectory = new File(projectDirectory, String.valueOf(id)); + var pId = new File(projectId).getName(); + int pathStart = baseDirectory.getAbsolutePath().length(); + var projectDirectory = getProjectDirectory(projectId); + var idDirectory = new File(projectDirectory, String.valueOf(id)); try (var op = new JobLock(idDirectory)) { - final var outputData = new ArrayList(); - for (final var output : outputs) { + var outputData = new ArrayList(); + for (var output : outputs) { if (!output.getAbsolutePath() .startsWith(baseDirectory.getAbsolutePath())) { throw new IOException("Output file " + output @@ -248,12 +248,14 @@ public List addOutputs(final String projectId, final int id, outputPath = outputPath.substring(1); } - final var newOutput = new File(idDirectory, outputPath); + var newOutput = new File(idDirectory, outputPath); newOutput.getParentFile().mkdirs(); if (newOutput.exists()) { if (!newOutput.delete()) { - logger.warn("Could not delete existing file {};" - + " new file will not be used!", newOutput); + logger.warn( + "Could not delete existing file {};" + + " new file will not be used!", + newOutput); } else { logger.warn("Overwriting existing file {}", newOutput); } @@ -261,11 +263,11 @@ public List addOutputs(final String projectId, final int id, if (!newOutput.exists()) { move(output.toPath(), newOutput.toPath()); } - final var outputUrl = new URL(baseServerUrl, + var outputUrl = new URL(baseServerUrl, "output/" + pId + "/" + id + "/" + outputPath); outputData.add(new DataItem(outputUrl.toExternalForm())); - logger.debug("New output {} mapped to {}", - newOutput, outputUrl); + logger.debug("New output {} mapped to {}", newOutput, + outputUrl); } return outputData; @@ -284,10 +286,10 @@ public List addOutputs(final String projectId, final int id, * file is downloaded, False to attempt to guess the content type * @return The response */ - private Response getResultFile(final File idDirectory, - final String filename, final boolean download) { - final var resultFile = new File(idDirectory, filename); - final var purgeFile = getPurgeFile(idDirectory); + private Response getResultFile(File idDirectory, String filename, + boolean download) { + var resultFile = new File(idDirectory, filename); + var purgeFile = getPurgeFile(idDirectory); try (var op = new JobLock(idDirectory)) { if (purgeFile.exists()) { @@ -304,14 +306,13 @@ private Response getResultFile(final File idDirectory, try { if (!download) { - final var contentType = - probeContentType(resultFile.toPath()); + var contentType = probeContentType(resultFile.toPath()); if (nonNull(contentType)) { logger.debug("File has content type {}", contentType); return ok(resultFile, contentType).build(); } } - } catch (final IOException e) { + } catch (IOException e) { logger.debug("Content type of {} could not be determined", resultFile, e); } @@ -328,24 +329,42 @@ private Response getResultFile(final File idDirectory, * The directory to find the file in * @return The purge marker file */ - private File getPurgeFile(final File directory) { + private File getPurgeFile(File directory) { return new File(resultsDirectory, PURGED_FILE + directory.getName()); } + /** + * Basic path validator. Rejects some things that could occur validly, but + * that's better than permitting an attack. + * + * @param path + * The path to check. + */ + private void validatePath(String path) { + Objects.requireNonNull(path); + if (path.isEmpty()) { + throw new IllegalArgumentException("bad path"); + } else if (path.contains("..")) { + throw new IllegalArgumentException("bad path"); + } + } + @Override - public Response getResultFile(final String projectId, final int id, - final String filename, final boolean download) { + public Response getResultFile(String projectId, int id, String filename, + boolean download) { logger.debug("Retrieving {} from {}/{}", filename, projectId, id); - final var projectDirectory = getProjectDirectory(projectId); - final var idDirectory = new File(projectDirectory, String.valueOf(id)); + validatePath(projectId); + validatePath(filename); + var projectDirectory = getProjectDirectory(projectId); + var idDirectory = new File(projectDirectory, String.valueOf(id)); return getResultFile(idDirectory, filename, download); } @Override - public Response getResultFile(final int id, final String filename, - final boolean download) { + public Response getResultFile(int id, String filename, boolean download) { logger.debug("Retrieving {} from {}", filename, id); - final var idDirectory = getProjectDirectory(String.valueOf(id)); + validatePath(filename); + var idDirectory = getProjectDirectory(String.valueOf(id)); return getResultFile(idDirectory, filename, download); } @@ -365,20 +384,19 @@ public Response getResultFile(final int id, final String filename, * @throws IOException * If something goes wrong */ - private void recursivelyUploadFiles(final String authHeader, - final File directory, - final UnicoreFileClient fileManager, final String storageId, - final String filePath) throws IOException { - final var files = directory.listFiles(); + private void recursivelyUploadFiles(String authHeader, File directory, + UnicoreFileClient fileManager, String storageId, String filePath) + throws IOException { + var files = directory.listFiles(); if (isNull(files)) { return; } - for (final var file : files) { + for (var file : files) { if (file.getName().equals(".") || file.getName().equals("..") || file.getName().isEmpty()) { continue; } - final var uploadFileName = filePath + "/" + file.getName(); + var uploadFileName = filePath + "/" + file.getName(); if (file.isDirectory()) { recursivelyUploadFiles(authHeader, file, fileManager, storageId, uploadFileName); @@ -390,20 +408,22 @@ private void recursivelyUploadFiles(final String authHeader, try (var input = new FileInputStream(file)) { fileManager.upload(authHeader, storageId, uploadFileName, input); - } catch (final WebApplicationException e) { + } catch (WebApplicationException e) { throw new IOException("Error uploading file to " + storageId + "/" + uploadFileName, e); - } catch (final FileNotFoundException e) { + } catch (FileNotFoundException e) { // Ignore files which vanish. } } } @Override - public Response uploadResultsToHPCServer(final String projectId, - final int id, final String serverUrl, final String storageId, - final String filePath, final String userId, final String token) { - final var idDirectory = + public Response uploadResultsToHPCServer(String projectId, int id, + String serverUrl, String storageId, String filePath, String userId, + String token) { + validatePath(projectId); + validatePath(filePath); + var idDirectory = new File(getProjectDirectory(projectId), String.valueOf(id)); if (!idDirectory.canRead()) { logger.debug("{} was not found", idDirectory); @@ -411,17 +431,17 @@ public Response uploadResultsToHPCServer(final String projectId, } try { - final var authHeader = "Bearer: " + token; - final var fileClient = UnicoreFileClient.createClient(serverUrl); + var authHeader = "Bearer: " + token; + var fileClient = UnicoreFileClient.createClient(serverUrl); try (var op = new JobLock(idDirectory)) { recursivelyUploadFiles(authHeader, idDirectory, fileClient, storageId, filePath.replaceAll("/+$", "")); } - } catch (final MalformedURLException e) { + } catch (MalformedURLException e) { logger.error("bad user-supplied URL", e); - return status(BAD_REQUEST) - .entity("The URL specified was malformed").build(); - } catch (final Throwable e) { + return status(BAD_REQUEST).entity("The URL specified was malformed") + .build(); + } catch (Throwable e) { logger.error("failure in upload", e); return serverError() .entity("General error reading or uploading a file") @@ -437,8 +457,8 @@ public Response uploadResultsToHPCServer(final String projectId, * @param directory * The directory to remove */ - private void removeDirectory(final File directory) { - for (final var file : directory.listFiles()) { + private void removeDirectory(File directory) { + for (var file : directory.listFiles()) { if (file.isDirectory()) { removeDirectory(file); } else { @@ -452,8 +472,8 @@ private void removeDirectory(final File directory) { * Remove files that are deemed to have expired. */ private void removeOldFiles() { - final long startTime = currentTimeMillis(); - for (final var projectDirectory : resultsDirectory.listFiles()) { + long startTime = currentTimeMillis(); + for (var projectDirectory : resultsDirectory.listFiles()) { if (projectDirectory.isDirectory() && removeOldProjectDirectoryContents(startTime, projectDirectory)) { @@ -473,15 +493,16 @@ && removeOldProjectDirectoryContents(startTime, * The directory containing the project files * @return True if every job in the project has been removed */ - private boolean removeOldProjectDirectoryContents(final long startTime, - final File projectDirectory) { + private boolean removeOldProjectDirectoryContents(long startTime, + File projectDirectory) { boolean allJobsRemoved = true; - for (final var jobDirectory : projectDirectory.listFiles()) { - logger.debug("Determining whether to remove {} " - + "which is {}ms old of {}", jobDirectory, - startTime - jobDirectory.lastModified(), + for (var jobDirectory : projectDirectory.listFiles()) { + logger.debug( + "Determining whether to remove {} which is {}ms old of {}", + jobDirectory, startTime - jobDirectory.lastModified(), timeToKeepResults); - if (jobDirectory.isDirectory() && ((startTime + if (jobDirectory + .isDirectory() && ((startTime - jobDirectory.lastModified()) > timeToKeepResults)) { logger.info("Removing results for job {}", jobDirectory.getName()); @@ -492,7 +513,7 @@ private boolean removeOldProjectDirectoryContents(final long startTime, try (var purgedFileWriter = new PrintWriter(getPurgeFile(jobDirectory))) { purgedFileWriter.println(currentTimeMillis()); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error writing purge file", e); } } else { diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/XenVMExecuterFactory.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/XenVMExecuterFactory.java index 62d09cc84e..3c173f2216 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/XenVMExecuterFactory.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/jobmanager/XenVMExecuterFactory.java @@ -37,6 +37,7 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; +import com.google.errorprone.annotations.concurrent.GuardedBy; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Types.VmPowerState; import com.xensource.xenapi.Types.XenAPIException; @@ -47,7 +48,7 @@ /** * Executer factory that uses Xen VMs. */ -public class XenVMExecuterFactory implements JobExecuterFactory { +public final class XenVMExecuterFactory implements JobExecuterFactory { /** Bytes in a gigabyte. Well, a gibibyte, but that's a nasty word. */ private static final long GB = 1024L * 1024L * 1024L; @@ -91,7 +92,7 @@ public class XenVMExecuterFactory implements JobExecuterFactory { @Value("${liveUploadOutput}") private boolean liveUploadOutput; - /** True if a spiNNaker machine should be requested. */ + /** True if a SpiNNaker machine should be requested. */ @Value("${requestSpiNNakerMachine}") private boolean requestSpiNNakerMachine; @@ -104,6 +105,7 @@ public class XenVMExecuterFactory implements JobExecuterFactory { private int maxNVirtualMachines; /** The current number of VMs. */ + @GuardedBy("lock") private int nVirtualMachines = 0; /** @@ -114,15 +116,15 @@ public XenVMExecuterFactory() { } @Override - public JobExecuter createJobExecuter(final JobManager manager, - final URL baseUrl) throws IOException { + public JobExecuter createJobExecuter(JobManager manager, URL baseUrl) + throws IOException { requireNonNull(manager); requireNonNull(baseUrl); waitToClaimVM(); try { return new Executer(manager, baseUrl); - } catch (final Exception e) { + } catch (Exception e) { throw new IOException("Error creating VM", e); } } @@ -172,8 +174,7 @@ class XenConnection implements AutoCloseable { * @throws XmlRpcException * something went wrong */ - XenConnection(final String id) - throws XenAPIException, XmlRpcException { + XenConnection(String id) throws XenAPIException, XmlRpcException { this.id = id; conn = new Connection(xenServerUrl); loginWithPassword(conn, username, password); @@ -189,12 +190,12 @@ class XenConnection implements AutoCloseable { * something went wrong */ VM getVirtualMachine() throws XmlRpcException, IOException { - final var vmsWithLabel = VM.getByNameLabel(conn, templateLabel); + var vmsWithLabel = VM.getByNameLabel(conn, templateLabel); if (vmsWithLabel.isEmpty()) { throw new IOException("No template with name " + templateLabel + " was found"); } - final var template = vmsWithLabel.iterator().next(); + var template = vmsWithLabel.iterator().next(); return template.createClone(conn, templateLabel + "_" + id); } @@ -209,9 +210,9 @@ VM getVirtualMachine() throws XmlRpcException, IOException { * @throws IOException * something went wrong */ - VBD getVirtualBlockDevice(final VM vm) + VBD getVirtualBlockDevice(VM vm) throws XmlRpcException, IOException { - final var disks = vm.getVBDs(conn); + var disks = vm.getVBDs(conn); if (disks.isEmpty()) { throw new IOException("No disks found on " + templateLabel); } @@ -231,7 +232,7 @@ VBD getVirtualBlockDevice(final VM vm) * @throws XenAPIException * something went wrong */ - String getLabel(final VDI vdi, final String suffix) + String getLabel(VDI vdi, String suffix) throws XmlRpcException, XenAPIException { return vdi.getNameLabel(conn) + "_" + id + "_" + suffix; } @@ -247,8 +248,8 @@ String getLabel(final VDI vdi, final String suffix) * @throws XenAPIException * something went wrong */ - VDI getBaseVDI(final VBD disk) throws XmlRpcException, XenAPIException { - final var vdi = disk.getVDI(conn); + VDI getBaseVDI(VBD disk) throws XmlRpcException, XenAPIException { + var vdi = disk.getVDI(conn); vdi.setNameLabel(conn, getLabel(vdi, "base")); return vdi; } @@ -264,9 +265,9 @@ VDI getBaseVDI(final VBD disk) throws XmlRpcException, XenAPIException { * @throws XmlRpcException * something went wrong */ - VDI createVDI(final VDI baseVDI) + VDI createVDI(VDI baseVDI) throws XenAPIException, XmlRpcException { - final var descriptor = new VDI.Record(); + var descriptor = new VDI.Record(); descriptor.nameLabel = getLabel(baseVDI, "storage"); descriptor.type = USER; descriptor.SR = baseVDI.getSR(conn); @@ -288,9 +289,9 @@ VDI createVDI(final VDI baseVDI) * @throws XmlRpcException * something went wrong */ - VBD createVBD(final VM vm, final VDI vdi) + VBD createVBD(VM vm, VDI vdi) throws XenAPIException, XmlRpcException { - final var descriptor = new VBD.Record(); + var descriptor = new VBD.Record(); descriptor.VM = vm; descriptor.VDI = vdi; descriptor.userdevice = "1"; @@ -314,7 +315,7 @@ VBD createVBD(final VM vm, final VDI vdi) * @throws XmlRpcException * something went wrong */ - void addData(final VM vm, final String key, final Object value) + void addData(VM vm, String key, Object value) throws XenAPIException, XmlRpcException { vm.addToXenstoreData(conn, key, value.toString()); } @@ -329,7 +330,7 @@ void addData(final VM vm, final String key, final Object value) * @throws XmlRpcException * something went wrong */ - void start(final VM vm) throws XenAPIException, XmlRpcException { + void start(VM vm) throws XenAPIException, XmlRpcException { vm.start(conn, false, true); } @@ -343,7 +344,7 @@ void start(final VM vm) throws XenAPIException, XmlRpcException { * @throws XmlRpcException * something went wrong */ - void destroy(final VM vm) throws XenAPIException, XmlRpcException { + void destroy(VM vm) throws XenAPIException, XmlRpcException { vm.destroy(conn); } @@ -357,7 +358,7 @@ void destroy(final VM vm) throws XenAPIException, XmlRpcException { * @throws XmlRpcException * something went wrong */ - void destroy(final VBD vbd) throws XenAPIException, XmlRpcException { + void destroy(VBD vbd) throws XenAPIException, XmlRpcException { vbd.destroy(conn); } @@ -371,7 +372,7 @@ void destroy(final VBD vbd) throws XenAPIException, XmlRpcException { * @throws XmlRpcException * something went wrong */ - void destroy(final VDI vdi) throws XenAPIException, XmlRpcException { + void destroy(VDI vdi) throws XenAPIException, XmlRpcException { vdi.destroy(conn); } @@ -386,7 +387,7 @@ void destroy(final VDI vdi) throws XenAPIException, XmlRpcException { * @throws XmlRpcException * something went wrong */ - VmPowerState getState(final VM vm) + VmPowerState getState(VM vm) throws XenAPIException, XmlRpcException { return vm.getPowerState(conn); } @@ -407,7 +408,7 @@ public void close() { /** * The executer core connector. */ - protected class Executer implements JobExecuter { + protected final class Executer implements JobExecuter { // Parameters from constructor /** The Job Manager to report to. */ private final JobManager jobManager; @@ -452,14 +453,14 @@ protected class Executer implements JobExecuter { * @throws IOException * something went wrong */ - Executer(final JobManager jobManager, final URL baseUrl) + Executer(JobManager jobManager, URL baseUrl) throws XmlRpcException, IOException { this.jobManager = jobManager; uuid = randomUUID().toString(); jobProcessManagerUrl = new URL(baseUrl, "job/" + JOB_PROCESS_MANAGER); - final var execArgs = new StringBuilder("-jar "); + var execArgs = new StringBuilder("-jar "); execArgs.append(JOB_PROCESS_MANAGER); execArgs.append(" --serverUrl "); execArgs.append(baseUrl); @@ -498,7 +499,7 @@ public void startExecuter() { * @throws IOException * something went wrong */ - synchronized void createVm(final XenConnection conn) + synchronized void createVm(XenConnection conn) throws XmlRpcException, IOException { clonedVm = conn.getVirtualMachine(); disk = conn.getVirtualBlockDevice(clonedVm); @@ -524,7 +525,7 @@ synchronized void createVm(final XenConnection conn) * @throws XmlRpcException * If there is an error speaking to Xen */ - private synchronized void deleteVm(final XenConnection conn) + private synchronized void deleteVm(XenConnection conn) throws XenAPIException, XmlRpcException { if (isNull(conn)) { return; @@ -556,7 +557,7 @@ private synchronized void deleteVm(final XenConnection conn) * @throws XmlRpcException * If there is an error speaking to Xen */ - private void waitForHalt(final XenConnection conn) + private void waitForHalt(XenConnection conn) throws XenAPIException, XmlRpcException { VmPowerState powerState; do { @@ -572,7 +573,7 @@ private void waitForHalt(final XenConnection conn) * @param conn * The connection to Xen */ - private void runInVm(final XenConnection conn) { + private void runInVm(XenConnection conn) { String action = null; try { action = "setting up VM"; @@ -580,7 +581,7 @@ private void runInVm(final XenConnection conn) { action = "getting VM power state; assuming off"; waitForHalt(conn); jobManager.setExecutorExited(uuid, null); - } catch (final Exception e) { + } catch (Exception e) { logger.error("Error " + action, e); jobManager.setExecutorExited(uuid, e.getMessage()); } finally { @@ -588,7 +589,7 @@ private void runInVm(final XenConnection conn) { if (deleteOnExit) { deleteVm(conn); } - } catch (final Exception e) { + } catch (Exception e) { logger.error("Error deleting VM", e); } } @@ -600,7 +601,7 @@ private void runInVm(final XenConnection conn) { private void runInVm() { try (var conn = new XenConnection(uuid)) { runInVm(conn); - } catch (final Exception e) { + } catch (Exception e) { logger.error("Error talking to Xen", e); jobManager.setExecutorExited(uuid, e.getMessage()); } finally { diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/FixedMachineManagerImpl.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/FixedMachineManagerImpl.java index 6e7d0e725a..7aaa22c816 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/FixedMachineManagerImpl.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/FixedMachineManagerImpl.java @@ -25,23 +25,27 @@ import org.springframework.beans.factory.annotation.Value; +import com.google.errorprone.annotations.concurrent.GuardedBy; + import uk.ac.manchester.spinnaker.nmpi.model.machine.ChipCoordinates; import uk.ac.manchester.spinnaker.nmpi.model.machine.SpinnakerMachine; /** * A manager of directly-connected SpiNNaker machines. */ -public class FixedMachineManagerImpl implements MachineManager { +public final class FixedMachineManagerImpl implements MachineManager { /** The queue of available machines. */ private final Set machinesAvailable = new HashSet<>(); /** The set of machine allocated. */ + @GuardedBy("lock") private final Set machinesAllocated = new HashSet<>(); /** Lock to avoid concurrent modification in different threads. */ private final Object lock = new Object(); /** True when the manager is finished. */ + @GuardedBy("lock") private boolean done = false; /** @@ -51,13 +55,15 @@ public class FixedMachineManagerImpl implements MachineManager { * the collection of machines to use */ @Value("${machines}") - void setInitialMachines(final List machines) { - machinesAvailable.addAll(machines); + void setInitialMachines(List machines) { + synchronized (lock) { + machinesAvailable.addAll(machines); + } } @Override public List getMachines() { - final var machines = new ArrayList(); + var machines = new ArrayList(); synchronized (lock) { machines.addAll(machinesAvailable); machines.addAll(machinesAllocated); @@ -66,11 +72,11 @@ public List getMachines() { } @Override - public SpinnakerMachine getNextAvailableMachine(final int nBoards, - final String owner, final int jobId) { + public SpinnakerMachine getNextAvailableMachine(int nBoards, String owner, + int jobId) { synchronized (lock) { while (!done) { - final var machine = getLargeEnoughMachine(nBoards); + var machine = getLargeEnoughMachine(nBoards); if (nonNull(machine)) { // Move the machine from available to allocated machinesAvailable.remove(machine); @@ -93,14 +99,15 @@ public SpinnakerMachine getNextAvailableMachine(final int nBoards, * The number of boards required. * @return A machine big enough, or null of none. */ - private SpinnakerMachine getLargeEnoughMachine(final int nBoards) { + @GuardedBy("lock") + private SpinnakerMachine getLargeEnoughMachine(int nBoards) { return machinesAvailable.stream() - .filter(machine -> machine.getnBoards() >= nBoards) - .findFirst().orElse(null); + .filter(machine -> machine.getnBoards() >= nBoards).findFirst() + .orElse(null); } @Override - public void releaseMachine(final SpinnakerMachine machine) { + public void releaseMachine(SpinnakerMachine machine) { synchronized (lock) { machinesAllocated.remove(machine); machinesAvailable.add(machine); @@ -117,31 +124,30 @@ public void close() { } @Override - public boolean isMachineAvailable(final SpinnakerMachine machine) { + public boolean isMachineAvailable(SpinnakerMachine machine) { synchronized (lock) { return !machinesAvailable.contains(machine); } } @Override - public boolean waitForMachineStateChange(final SpinnakerMachine machine, - final int waitTime) { + public boolean waitForMachineStateChange(SpinnakerMachine machine, + int waitTime) { synchronized (lock) { - final boolean isAvailable = machinesAvailable.contains(machine); + boolean isAvailable = machinesAvailable.contains(machine); waitfor(lock, waitTime); return machinesAvailable.contains(machine) != isAvailable; } } @Override - public void setMachinePower( - final SpinnakerMachine machine, final boolean powerOn) { + public void setMachinePower(SpinnakerMachine machine, boolean powerOn) { // Does Nothing in this implementation } @Override - public ChipCoordinates getChipCoordinates(final SpinnakerMachine machine, - final int x, final int y) { + public ChipCoordinates getChipCoordinates(SpinnakerMachine machine, int x, + int y) { return new ChipCoordinates(0, 0, 0); } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/MachineManager.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/MachineManager.java index b00437f54c..1c6f045368 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/MachineManager.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/MachineManager.java @@ -24,7 +24,9 @@ /** * A service for managing SpiNNaker boards in a machine. */ -public interface MachineManager extends AutoCloseable { +public sealed interface MachineManager extends AutoCloseable + permits FixedMachineManagerImpl, SpallocJavaMachineManagerImpl, + SpallocMachineManagerImpl { /** * Gets the machines that this manager allocates from. * diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/SpallocJavaMachineManagerImpl.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/SpallocJavaMachineManagerImpl.java index 1c093a3a2d..7a73c5c5de 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/SpallocJavaMachineManagerImpl.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/SpallocJavaMachineManagerImpl.java @@ -16,16 +16,16 @@ package uk.ac.manchester.spinnaker.nmpi.machinemanager; import static java.util.Objects.isNull; +import static java.util.stream.Collectors.toList; import java.io.IOException; import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; -import javax.annotation.PostConstruct; -import javax.ws.rs.core.UriBuilder; +import jakarta.annotation.PostConstruct; +import jakarta.ws.rs.core.UriBuilder; import org.springframework.beans.factory.annotation.Value; @@ -40,7 +40,7 @@ /** * A machine manager that interfaces to the new spalloc service. */ -public class SpallocJavaMachineManagerImpl implements MachineManager { +public final class SpallocJavaMachineManagerImpl implements MachineManager { /** The version to use for the boards. */ private static final String VERSION = "5"; @@ -50,7 +50,10 @@ public class SpallocJavaMachineManagerImpl implements MachineManager { /** The reason to use when a job is finished. */ private static final String REASON_FINISHED = "Finished"; - /** The URI of the spalloc server. */ + /** + * The URI of the spalloc server. Includes user/password + * credentials. + */ @Value("${spalloc.server}") private URI spallocUri; @@ -85,16 +88,16 @@ public void close() throws Exception { @Override public List getMachines() throws IOException { - return client.listMachines().stream().map( - m -> new SpinnakerMachine(m.getName(), VERSION, + return client.listMachines().stream() + .map(m -> new SpinnakerMachine(m.getName(), VERSION, m.getWidth(), m.getHeight(), m.getLiveBoardCount(), null)) - .collect(Collectors.toList()); + .collect(toList()); } @Override - public SpinnakerMachine getNextAvailableMachine(final int nBoards, - final String owner, final int jobId) { + public SpinnakerMachine getNextAvailableMachine(int nBoards, String owner, + int jobId) { try { var createJob = new CreateJob(nBoards); createJob.setOwner(owner); @@ -114,24 +117,20 @@ public SpinnakerMachine getNextAvailableMachine(final int nBoards, } @Override - public boolean isMachineAvailable(final SpinnakerMachine machine) { - final var job = jobMap.get(machine); - if (isNull(job)) { - return false; - } - return true; + public boolean isMachineAvailable(SpinnakerMachine machine) { + return jobMap.containsKey(machine); } @Override - public boolean waitForMachineStateChange(final SpinnakerMachine machine, - final int waitTime) { - final var job = jobMap.get(machine); + public boolean waitForMachineStateChange(SpinnakerMachine machine, + int waitTime) { + var job = jobMap.get(machine); if (isNull(job)) { return true; } - final var state = lastJobState.get(machine); + var state = lastJobState.get(machine); try { - final var newState = job.describe(true).getState(); + var newState = job.describe(true).getState(); lastJobState.put(machine, newState); return newState == state; } catch (IOException e) { @@ -140,8 +139,8 @@ public boolean waitForMachineStateChange(final SpinnakerMachine machine, } @Override - public void releaseMachine(final SpinnakerMachine machine) { - final var job = jobMap.get(machine); + public void releaseMachine(SpinnakerMachine machine) { + var job = jobMap.get(machine); if (isNull(job)) { return; } @@ -154,9 +153,8 @@ public void releaseMachine(final SpinnakerMachine machine) { } @Override - public void setMachinePower(final SpinnakerMachine machine, - final boolean powerOn) { - final var job = jobMap.get(machine); + public void setMachinePower(SpinnakerMachine machine, boolean powerOn) { + var job = jobMap.get(machine); if (isNull(job)) { return; } @@ -168,19 +166,18 @@ public void setMachinePower(final SpinnakerMachine machine, } @Override - public ChipCoordinates getChipCoordinates(final SpinnakerMachine machine, - final int x, final int y) { - final var job = jobMap.get(machine); + public ChipCoordinates getChipCoordinates(SpinnakerMachine machine, int x, + int y) { + var job = jobMap.get(machine); if (isNull(job)) { return null; } try { - var whereIs = job.whereIs(new ChipLocation(x, y)) - .getPhysicalCoords(); - return new ChipCoordinates(whereIs.c, whereIs.f, whereIs.b); + var whereIs = + job.whereIs(new ChipLocation(x, y)).getPhysicalCoords(); + return new ChipCoordinates(whereIs.c(), whereIs.f(), whereIs.b()); } catch (IOException e) { throw new RuntimeException(e); } } - } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/SpallocMachineManagerImpl.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/SpallocMachineManagerImpl.java index 013a771cfb..080f8e43a2 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/SpallocMachineManagerImpl.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/SpallocMachineManagerImpl.java @@ -17,7 +17,6 @@ import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE; -import static java.util.Arrays.asList; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.concurrent.Executors.newScheduledThreadPool; @@ -36,17 +35,16 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; @@ -81,7 +79,7 @@ /** * A machine manager that interfaces to the old spalloc service. */ -public class SpallocMachineManagerImpl implements MachineManager { +public final class SpallocMachineManagerImpl implements MachineManager { /** The default version of a machine. */ private static final String MACHINE_VERSION = "5"; @@ -163,7 +161,7 @@ private static final class ResponseDeserializer * Make a machine manager that talks to Spalloc to do its work. */ public SpallocMachineManagerImpl() { - final var module = new SimpleModule(); + var module = new SimpleModule(); module.addDeserializer(Response.class, new ResponseDeserializer()); mapper.registerModule(module); mapper.setPropertyNamingStrategy(SNAKE_CASE); @@ -175,19 +173,19 @@ public SpallocMachineManagerImpl() { */ @PostConstruct private void startThreads() { - final var group = new ThreadGroup("Spalloc"); + var group = new ThreadGroup("Spalloc"); scheduler = newScheduledThreadPool(1, r -> new Thread(group, r, "Spalloc Keep Alive Handler")); new Thread(group, this::comms, "Spalloc Comms Interface").start(); - final var t = new Thread(group, this::updateStateOfJobs, + var t = new Thread(group, this::updateStateOfJobs, "Spalloc JobState Update Notification Handler"); t.setDaemon(true); t.start(); - scheduler.scheduleAtFixedRate(this::keepAllJobsAlive, - PERIOD, PERIOD, SECONDS); + scheduler.scheduleAtFixedRate(this::keepAllJobsAlive, PERIOD, PERIOD, + SECONDS); } // ------------------------------ COMMS ------------------------------ @@ -208,7 +206,7 @@ class TimeoutException extends IOException { * @param message * The message of the exception */ - TimeoutException(final String message) { + TimeoutException(String message) { super(message); } } @@ -244,7 +242,7 @@ class TimeoutException extends IOException { * @throws IOException * If the response failed to be read. */ - private T getNextResponse(final Class responseType) + private T getNextResponse(Class responseType) throws IOException { Response response; try { @@ -253,23 +251,20 @@ private T getNextResponse(final Class responseType) throw new TimeoutException( "No response from spalloc server"); } - } catch (final InterruptedException e) { + } catch (InterruptedException e) { return null; } - if (response instanceof ExceptionResponse) { - throw new IOException( - ((ExceptionResponse) response).getException()); - } - if (response instanceof ReturnResponse) { + if (response instanceof ExceptionResponse er) { + throw new IOException(er.getException()); + } else if (response instanceof ReturnResponse rr) { if (isNull(responseType)) { return null; } - return mapper.readValue( - ((ReturnResponse) response).getReturnValue(), - responseType); + return mapper.readValue(rr.getReturnValue(), responseType); + } else { + // Should never happen! + throw new IOException("Unknown Response " + response); } - // Should never happen! - throw new IOException("Unknown Response " + response); } /** @@ -292,7 +287,7 @@ private synchronized void waitForConnection() { * @throws IOException * If an error occurs */ - private void writeRequest(final Command request) throws IOException { + private void writeRequest(Command request) throws IOException { var message = mapper.writeValueAsString(request); logger.trace("Sending message {}", message); writer.println(message); @@ -307,7 +302,7 @@ private void writeRequest(final Command request) throws IOException { */ private void readResponse() throws IOException { // Note, assumes one response per line - final var line = reader.readLine(); + var line = reader.readLine(); if (isNull(line)) { synchronized (this) { connected = false; @@ -317,13 +312,13 @@ private void readResponse() throws IOException { } logger.trace("Received response: {}", line); - final var response = mapper.readValue(line, Response.class); + var response = mapper.readValue(line, Response.class); logger.trace("Received response of type {}", response); if (response instanceof ReturnResponse || response instanceof ExceptionResponse) { responses.offer(response); - } else if (response instanceof JobsChangedResponse) { - notifications.offer((JobsChangedResponse) response); + } else if (response instanceof JobsChangedResponse jc) { + notifications.offer(jc); } else { logger.error("Unrecognized response: {}", response); } @@ -343,7 +338,7 @@ public void mainLoop() { while (!done) { try { connect(); - } catch (final IOException e) { + } catch (IOException e) { if (!done) { logger.error("Could not connect to machine server", e); } @@ -352,7 +347,7 @@ public void mainLoop() { while (connected) { readResponse(); } - } catch (final IOException e) { + } catch (IOException e) { if (!done) { logger.error("Error receiving", e); disconnect(); @@ -405,8 +400,8 @@ public void disconnect() { * @throws IOException * If anything goes wrong */ - public T sendRequest(final Command request, - final Class responseType) throws IOException { + public T sendRequest(Command request, Class responseType) + throws IOException { synchronized (SpallocMachineManagerImpl.this) { int count = 0; while (true) { @@ -414,7 +409,7 @@ public T sendRequest(final Command request, waitForConnection(); writeRequest(request); return getNextResponse(responseType); - } catch (final IOException e) { + } catch (IOException e) { // Disconnect on an error to force reconnection disconnect(); if (++count >= N_RETRIES) { @@ -434,7 +429,7 @@ public T sendRequest(final Command request, * @throws IOException * If anything goes wrong */ - public void sendRequest(final Command request) throws IOException { + public void sendRequest(Command request) throws IOException { sendRequest(request, null); } @@ -483,7 +478,7 @@ final class SpallocJob { * @param jobId * The ID code of the job. */ - SpallocJob(final int jobId) { + SpallocJob(int jobId) { this.id = jobId; } @@ -519,7 +514,7 @@ JobState getState() throws IOException { * @throws IOException * If anything goes wrong */ - void notify(final boolean enable) throws IOException { + void notify(boolean enable) throws IOException { if (enable) { comms.sendRequest(new NotifyJobCommand(id)); } else { @@ -555,7 +550,7 @@ void destroy() throws IOException { * @throws IOException * If anything goes wrong */ - void power(final boolean powerOn) throws IOException { + void power(boolean powerOn) throws IOException { if (powerOn) { comms.sendRequest(new PowerOnJobBoardsCommand(id)); } else { @@ -574,19 +569,19 @@ void power(final boolean powerOn) throws IOException { * @throws IOException * If anything goes wrong */ - WhereIs whereIs(final int chipX, final int chipY) throws IOException { + WhereIs whereIs(int chipX, int chipY) throws IOException { return comms.sendRequest(new WhereIsCommand(id, chipX, chipY), WhereIs.class); } @Override public int hashCode() { - return id | MAGIC; + return id ^ MAGIC; } @Override - public boolean equals(final Object o) { - return (o instanceof SpallocJob) && (((SpallocJob) o).id == id); + public boolean equals(Object o) { + return (o instanceof SpallocJob job) && (job.id == id); } } @@ -597,7 +592,7 @@ public boolean equals(final Object o) { * @throws IOException * If anything goes wrong */ - final Machine[] listMachines() throws IOException { + Machine[] listMachines() throws IOException { return comms.sendRequest(new ListMachinesCommand(), Machine[].class); } @@ -612,8 +607,7 @@ final Machine[] listMachines() throws IOException { * @throws IOException * If anything goes wrong */ - final SpallocJob createJob(final int nBoards, final String jobOwner) - throws IOException { + SpallocJob createJob(int nBoards, String jobOwner) throws IOException { return new SpallocJob(comms.sendRequest( new CreateJobCommand(nBoards, owner + ":" + jobOwner), Integer.class)); @@ -629,8 +623,8 @@ final SpallocJob createJob(final int nBoards, final String jobOwner) * @throws IOException * If there is an error getting the state */ - private void updateJobState(final SpallocJob job) throws IOException { - final JobState state; + private void updateJobState(SpallocJob job) throws IOException { + JobState state; synchronized (machineState) { logger.debug("Getting state of {}", job.id); state = job.getState(); @@ -640,7 +634,7 @@ private void updateJobState(final SpallocJob job) throws IOException { } if (state.getState() == DESTROYED) { - final var machine = machinesAllocated.remove(job.id); + var machine = machinesAllocated.remove(job.id); if (isNull(machine)) { logger.error("Unrecognized job: {}", job); return; @@ -658,9 +652,9 @@ private void updateJobState(final SpallocJob job) throws IOException { * @throws IOException * If an I/O error occurs */ - private SpinnakerMachine getMachineForJob(final SpallocJob job) + private SpinnakerMachine getMachineForJob(SpallocJob job) throws IOException { - final var info = job.getMachineInfo(); + var info = job.getMachineInfo(); return new SpinnakerMachine(info.getConnections().get(0).getHostname(), MACHINE_VERSION, info.getWidth(), info.getHeight(), info.getConnections().size(), null); @@ -677,11 +671,11 @@ private SpinnakerMachine getMachineForJob(final SpallocJob job) * @throws IOException * If an I/O error occurs */ - private JobState waitForStates(final SpallocJob job, - final Integer... states) throws IOException { - final var set = new HashSet<>(asList(states)); + private JobState waitForStates(SpallocJob job, Integer... states) + throws IOException { + var set = Set.of(states); synchronized (machineState) { - final var state = job.getState(); + var state = job.getState(); machineState.put(job.id, state); while (!machineState.containsKey(job.id) || !set.contains(machineState.get(job.id).getState())) { @@ -705,15 +699,15 @@ public List getMachines() { m.getHeight() * MACHINE_HEIGHT_FACTOR, m.getWidth() * m.getHeight(), null)) .collect(toList()); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error getting machines", e); return null; } } @Override - public SpinnakerMachine getNextAvailableMachine(final int nBoards, - final String jobOwner, final int jobId) { + public SpinnakerMachine getNextAvailableMachine(int nBoards, + String jobOwner, int jobId) { SpallocJob job = null; SpinnakerMachine machineAllocated = null; @@ -721,7 +715,7 @@ public SpinnakerMachine getNextAvailableMachine(final int nBoards, try { job = startJob(nBoards, jobOwner); machineAllocated = getMachineForJob(job); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error getting machine - retrying", e); } } @@ -731,7 +725,7 @@ public SpinnakerMachine getNextAvailableMachine(final int nBoards, return machineAllocated; } - private SpallocJob startJob(final int nBoards, final String jobOwner) + private SpallocJob startJob(int nBoards, String jobOwner) throws IOException { var job = createJob(nBoards, jobOwner); logger.debug("Got machine {}, requesting notifications", job.id); @@ -750,18 +744,18 @@ private SpallocJob startJob(final int nBoards, final String jobOwner) } @Override - public void releaseMachine(final SpinnakerMachine machine) { - final var job = jobByMachine.remove(machine); + public void releaseMachine(SpinnakerMachine machine) { + var job = jobByMachine.remove(machine); try { if (nonNull(job)) { stopJob(job); } - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error releasing machine for {}", job.id); } } - private void stopJob(final SpallocJob job) throws IOException { + private void stopJob(SpallocJob job) throws IOException { logger.debug("Turning off notification for {}", job.id); job.notify(false); logger.debug("Notifications for {} are off", job.id); @@ -774,8 +768,8 @@ private void stopJob(final SpallocJob job) throws IOException { } @Override - public boolean isMachineAvailable(final SpinnakerMachine machine) { - final var job = jobByMachine.get(machine); + public boolean isMachineAvailable(SpinnakerMachine machine) { + var job = jobByMachine.get(machine); if (isNull(job)) { return false; } @@ -784,25 +778,24 @@ public boolean isMachineAvailable(final SpinnakerMachine machine) { } @Override - public boolean waitForMachineStateChange(final SpinnakerMachine machine, - final int waitTime) { - final var job = jobByMachine.get(machine); + public boolean waitForMachineStateChange(SpinnakerMachine machine, + int waitTime) { + var job = jobByMachine.get(machine); if (isNull(job)) { return true; } synchronized (machineState) { - final var state = machineState.get(job.id); + var state = machineState.get(job.id); waitfor(machineState, waitTime); - final var newState = machineState.get(job.id); + var newState = machineState.get(job.id); return nonNull(newState) && newState.equals(state); } } @Override - public void setMachinePower(final SpinnakerMachine machine, - final boolean powerOn) { - final var job = jobByMachine.get(machine); + public void setMachinePower(SpinnakerMachine machine, boolean powerOn) { + var job = jobByMachine.get(machine); if (isNull(job)) { return; } @@ -819,15 +812,15 @@ public void setMachinePower(final SpinnakerMachine machine, } @Override - public ChipCoordinates getChipCoordinates(final SpinnakerMachine machine, - final int x, final int y) { - final var job = jobByMachine.get(machine); + public ChipCoordinates getChipCoordinates(SpinnakerMachine machine, int x, + int y) { + var job = jobByMachine.get(machine); if (isNull(job)) { return null; } try { var whereIs = job.whereIs(x, y); - int[] location = whereIs.getPhysical(); + var location = whereIs.getPhysical(); return new ChipCoordinates(location[0], location[1], location[2]); } catch (IOException e) { throw new RuntimeException("Error getting coordinates", e); @@ -840,12 +833,12 @@ public ChipCoordinates getChipCoordinates(final SpinnakerMachine machine, private void keepAllJobsAlive() { List jobIds; synchronized (machineState) { - jobIds = new ArrayList<>(machineState.keySet()); + jobIds = List.copyOf(machineState.keySet()); } - for (final int jobId : jobIds) { + for (int jobId : jobIds) { try { new SpallocJob(jobId).keepAlive(); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error keeping machine {} alive", jobId); } } @@ -857,15 +850,15 @@ private void keepAllJobsAlive() { private void updateStateOfJobs() { try { while (!done) { - for (final int jobId : comms.getJobsChanged()) { + for (int jobId : comms.getJobsChanged()) { try { updateJobState(new SpallocJob(jobId)); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error getting job state", e); } } } - } catch (final InterruptedException e) { + } catch (InterruptedException e) { logger.warn("interrupt of job state updating"); } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/Command.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/Command.java index 53344b0957..ebb4f4ec76 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/Command.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/Command.java @@ -26,7 +26,11 @@ * @param * The type of arguments. */ -public abstract class Command { +public abstract sealed class Command + permits CreateJobCommand, DestroyJobCommand, GetJobMachineInfoCommand, + GetJobStateCommand, JobKeepAliveCommand, ListMachinesCommand, + NoNotifyJobCommand, NotifyJobCommand, PowerOffJobBoardsCommand, + PowerOnJobBoardsCommand, WhereIsCommand { /** The name of the command. */ private final String command; @@ -44,7 +48,7 @@ public abstract class Command { * @param value * The argument value; will be converted to a string */ - protected final void addKwArg(final String key, final Object value) { + protected final void addKwArg(String key, Object value) { kwargs.put(key, value); } @@ -55,8 +59,8 @@ protected final void addKwArg(final String key, final Object value) { * The arguments to add. */ @SafeVarargs - protected final void addArg(final A... values) { - for (final A value : values) { + protected final void addArg(A... values) { + for (var value : values) { args.add(value); } } @@ -67,7 +71,7 @@ protected final void addArg(final A... values) { * @param command * The command token. */ - public Command(final String command) { + protected Command(String command) { this.command = command; } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/CreateJobCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/CreateJobCommand.java index 5e80ee348c..d08d793e28 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/CreateJobCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/CreateJobCommand.java @@ -18,7 +18,7 @@ /** * Request to create a job. */ -public class CreateJobCommand extends Command { +public final class CreateJobCommand extends Command { /** * Create a request to create a job. * @@ -27,7 +27,7 @@ public class CreateJobCommand extends Command { * @param owner * The owner of the job to create. */ - public CreateJobCommand(final int numBoards, final String owner) { + public CreateJobCommand(int numBoards, String owner) { super("create_job"); addArg(numBoards); addKwArg("owner", owner); diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/DestroyJobCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/DestroyJobCommand.java index dbafe7a246..c0f393b582 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/DestroyJobCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/DestroyJobCommand.java @@ -18,14 +18,14 @@ /** * Request to destroy a job. */ -public class DestroyJobCommand extends Command { +public final class DestroyJobCommand extends Command { /** * Make a request to destroy a job. * * @param jobId * The ID of the job. */ - public DestroyJobCommand(final int jobId) { + public DestroyJobCommand(int jobId) { super("destroy_job"); addArg(jobId); } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/GetJobMachineInfoCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/GetJobMachineInfoCommand.java index 833898119e..240ee2d15e 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/GetJobMachineInfoCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/GetJobMachineInfoCommand.java @@ -18,14 +18,14 @@ /** * Request to get machine information relating to a job. */ -public class GetJobMachineInfoCommand extends Command { +public final class GetJobMachineInfoCommand extends Command { /** * Create a request to get information about a job's allocated machine. * * @param jobId * The job to ask about. */ - public GetJobMachineInfoCommand(final int jobId) { + public GetJobMachineInfoCommand(int jobId) { super("get_job_machine_info"); addArg(jobId); } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/GetJobStateCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/GetJobStateCommand.java index a41267e6b8..be76a03dc3 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/GetJobStateCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/GetJobStateCommand.java @@ -18,14 +18,14 @@ /** * Request the state of a job. */ -public class GetJobStateCommand extends Command { +public final class GetJobStateCommand extends Command { /** * Create a request to get the state of a job. * * @param jobId * The job to get the state of. */ - public GetJobStateCommand(final int jobId) { + public GetJobStateCommand(int jobId) { super("get_job_state"); addArg(jobId); } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/JobKeepAliveCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/JobKeepAliveCommand.java index 8146931363..5e1230f405 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/JobKeepAliveCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/JobKeepAliveCommand.java @@ -18,14 +18,14 @@ /** * Request to keep a job alive. */ -public class JobKeepAliveCommand extends Command { +public final class JobKeepAliveCommand extends Command { /** * Create a request to keep a job alive. * * @param jobId * The job to ask about. */ - public JobKeepAliveCommand(final int jobId) { + public JobKeepAliveCommand(int jobId) { super("job_keepalive"); addArg(jobId); } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/ListMachinesCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/ListMachinesCommand.java index 12b721e3a9..f879cec4e1 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/ListMachinesCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/ListMachinesCommand.java @@ -18,7 +18,7 @@ /** * Request to get the known machines from the spalloc service. */ -public class ListMachinesCommand extends Command { +public final class ListMachinesCommand extends Command { /** Create a request to list the known SpiNNaker machines. */ public ListMachinesCommand() { super("list_machines"); diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/NoNotifyJobCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/NoNotifyJobCommand.java index f191857567..8791555c2b 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/NoNotifyJobCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/NoNotifyJobCommand.java @@ -18,14 +18,14 @@ /** * Request to not receive notifications about a job. */ -public class NoNotifyJobCommand extends Command { +public final class NoNotifyJobCommand extends Command { /** * Create a request to not be notified of changes in job state. * * @param jobId * The job to request about. */ - public NoNotifyJobCommand(final int jobId) { + public NoNotifyJobCommand(int jobId) { super("no_notify_job"); addArg(jobId); } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/NotifyJobCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/NotifyJobCommand.java index be467dc6ec..dae309e4ec 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/NotifyJobCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/NotifyJobCommand.java @@ -18,14 +18,14 @@ /** * Request to get notifications about a job. */ -public class NotifyJobCommand extends Command { +public final class NotifyJobCommand extends Command { /** * Create a request to be notified of changes in job state. * * @param jobId * The job to request about. */ - public NotifyJobCommand(final int jobId) { + public NotifyJobCommand(int jobId) { super("notify_job"); addArg(jobId); } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/PowerOffJobBoardsCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/PowerOffJobBoardsCommand.java index c7a16d6192..b294de90d0 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/PowerOffJobBoardsCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/PowerOffJobBoardsCommand.java @@ -18,14 +18,14 @@ /** * Request to turn off the boards associated with a job. */ -public class PowerOffJobBoardsCommand extends Command { +public final class PowerOffJobBoardsCommand extends Command { /** * Create a request to turn off a job's allocated boards. * * @param jobId * The job to request about. */ - public PowerOffJobBoardsCommand(final int jobId) { + public PowerOffJobBoardsCommand(int jobId) { super("power_off_job_boards"); addArg(jobId); } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/PowerOnJobBoardsCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/PowerOnJobBoardsCommand.java index 448015a843..e338860827 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/PowerOnJobBoardsCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/PowerOnJobBoardsCommand.java @@ -18,14 +18,14 @@ /** * Request to turn on the boards associated with a job. */ -public class PowerOnJobBoardsCommand extends Command { +public final class PowerOnJobBoardsCommand extends Command { /** * Create a request to turn on a job's allocated boards. * * @param jobId * The job to request about. */ - public PowerOnJobBoardsCommand(final int jobId) { + public PowerOnJobBoardsCommand(int jobId) { super("power_on_job_boards"); addArg(jobId); } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/WhereIsCommand.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/WhereIsCommand.java index b68eb1e52b..dea276863a 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/WhereIsCommand.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/commands/WhereIsCommand.java @@ -19,7 +19,7 @@ * Request to get the location of a chip in a job's allocation relative to a * machine. */ -public class WhereIsCommand extends Command { +public final class WhereIsCommand extends Command { /** * Create a request to locate a chip within a job's allocation. * @@ -30,7 +30,7 @@ public class WhereIsCommand extends Command { * @param chipY * The Y coordinate of the chip to ask about. */ - public WhereIsCommand(final int jobId, final int chipX, final int chipY) { + public WhereIsCommand(int jobId, int chipX, int chipY) { super("where_is"); addKwArg("job_id", jobId); addKwArg("chip_x", chipX); diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Chip.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Chip.java index bfd6ae257b..317fa24652 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Chip.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Chip.java @@ -33,9 +33,9 @@ public class Chip { private int y; /** - * Get the x-coordinate of the chip. + * Get the X-coordinate of the chip. * - * @return The x-coordinate of the chip + * @return The X-coordinate of the chip */ public int getX() { return x; @@ -47,14 +47,14 @@ public int getX() { * @param x * The x-coordinate of the chip */ - public void setX(final int x) { + void setX(int x) { this.x = x; } /** - * Get the y-coordinate of the chip. + * Get the Y-coordinate of the chip. * - * @return The y-coordinate of the chip + * @return The Y-coordinate of the chip */ public int getY() { return y; @@ -66,7 +66,7 @@ public int getY() { * @param y * The y-coordinate of the chip */ - public void setY(final int y) { + void setY(int y) { this.y = y; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Connection.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Connection.java index 9b1bc3ff27..ed338e4505 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Connection.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Connection.java @@ -47,7 +47,7 @@ public Chip getChip() { * @param chip * The chip to set */ - public void setChip(final Chip chip) { + void setChip(Chip chip) { this.chip = chip; } @@ -66,7 +66,7 @@ public String getHostname() { * @param hostname * The host name to set */ - public void setHostname(final String hostname) { + void setHostname(String hostname) { this.hostname = hostname; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ExceptionResponse.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ExceptionResponse.java index fb80c06de8..586716292b 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ExceptionResponse.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ExceptionResponse.java @@ -21,7 +21,7 @@ /** * A response to a request that indicates a failure. */ -public class ExceptionResponse implements Response { +public final class ExceptionResponse implements Response { /** The exception to report. */ private String exception; @@ -41,7 +41,7 @@ public String getException() { * The exception to set */ @JsonSetter("exception") - public void setException(final JsonNode exception) { + void setException(JsonNode exception) { this.exception = exception.toString(); } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobMachineInfo.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobMachineInfo.java index efca8b4faa..a0706b536d 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobMachineInfo.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobMachineInfo.java @@ -49,7 +49,7 @@ public int getWidth() { * @param width * The width in chips */ - public void setWidth(final int width) { + void setWidth(int width) { this.width = width; } @@ -68,7 +68,7 @@ public int getHeight() { * @param height * The height in chips */ - public void setHeight(final int height) { + void setHeight(int height) { this.height = height; } @@ -87,7 +87,7 @@ public List getConnections() { * @param connections * The connections to set */ - public void setConnections(final List connections) { + void setConnections(List connections) { this.connections = connections; } @@ -106,7 +106,7 @@ public String getMachineName() { * @param machineName * The name to set */ - public void setMachineName(final String machineName) { + void setMachineName(String machineName) { this.machineName = machineName; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobState.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobState.java index 6bcafade5f..2792900c64 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobState.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobState.java @@ -63,7 +63,7 @@ public int getState() { * @param state * The state to set */ - public void setState(final int state) { + void setState(int state) { this.state = state; } @@ -82,7 +82,7 @@ public Boolean getPower() { * @param power * True for on, False for off */ - public void setPower(final Boolean power) { + void setPower(Boolean power) { this.power = power; } @@ -101,7 +101,7 @@ public double getKeepAlive() { * @param keepAlive * The number of seconds to set */ - public void setKeepAlive(final double keepAlive) { + void setKeepAlive(double keepAlive) { this.keepAlive = keepAlive; } @@ -120,7 +120,7 @@ public String getReason() { * @param reason * The reason to set. */ - public void setReason(final String reason) { + void setReason(String reason) { this.reason = reason; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobsChangedResponse.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobsChangedResponse.java index d824346dcb..8c017d0077 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobsChangedResponse.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/JobsChangedResponse.java @@ -22,7 +22,7 @@ /** * A response that describes what jobs have changed state. */ -public class JobsChangedResponse implements Response { +public final class JobsChangedResponse implements Response { /** The list of jobs that have changed. */ private List jobsChanged = emptyList(); @@ -41,7 +41,7 @@ public List getJobsChanged() { * @param jobsChanged * The list of job ids */ - public void setJobsChanged(final List jobsChanged) { + void setJobsChanged(List jobsChanged) { this.jobsChanged = jobsChanged; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ListMachinesResponse.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ListMachinesResponse.java index 565492a2bd..b0284d20ac 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ListMachinesResponse.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ListMachinesResponse.java @@ -44,7 +44,7 @@ public List getMachines() { * @param machines * The list of machines to set */ - public void setMachines(final List machines) { + void setMachines(List machines) { this.machines = machines; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Machine.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Machine.java index 9c9868b7b4..a8fba02611 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Machine.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Machine.java @@ -48,7 +48,7 @@ public String getName() { * @param name * The name of the machine to set */ - public void setName(final String name) { + void setName(String name) { this.name = name; } @@ -67,7 +67,7 @@ public List getTags() { * @param tags * The tags to set */ - public void setTags(final List tags) { + void setTags(List tags) { this.tags = tags; } @@ -86,7 +86,7 @@ public int getWidth() { * @param width * The width in chips */ - public void setWidth(final int width) { + void setWidth(int width) { this.width = width; } @@ -105,7 +105,7 @@ public int getHeight() { * @param height * The height in chips */ - public void setHeight(final int height) { + void setHeight(int height) { this.height = height; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Response.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Response.java index bd996298ce..d32da7f35f 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Response.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/Response.java @@ -18,6 +18,7 @@ /** * An (abstract) response from the machine manager. Responses are all POJOs. */ -public interface Response { +public sealed interface Response + permits ReturnResponse, ExceptionResponse, JobsChangedResponse { // Does Nothing } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ReturnResponse.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ReturnResponse.java index d609ac2252..dcc2c07060 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ReturnResponse.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/ReturnResponse.java @@ -21,7 +21,7 @@ /** * A response that is the successful result of a request. */ -public class ReturnResponse implements Response { +public final class ReturnResponse implements Response { /** The value returned. */ private String returnValue; @@ -41,7 +41,7 @@ public String getReturnValue() { * The value to set */ @JsonSetter("return") - public void setReturnValue(final JsonNode returnValue) { + void setReturnValue(JsonNode returnValue) { this.returnValue = returnValue.toString(); } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/WhereIs.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/WhereIs.java index 663954fa4f..19041218b7 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/WhereIs.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/machinemanager/responses/WhereIs.java @@ -19,40 +19,25 @@ * The description of where some resource is on a SpiNNaker system. */ public class WhereIs { - - /** - * The job-relative location of the chip. - */ + /** The job-relative location of the chip. */ private int[] jobChip; - /** - * The id of the job. - */ + /** The id of the job. */ private int jobId; - /** - * The physical location of the chip. - */ + /** The physical location of the chip. */ private int[] chip; - /** - * The logical location of the job. - */ + /** The logical location of the job. */ private int[] logical; - /** - * The machine if the job. - */ + /** The machine if the job. */ private String machine; - /** - * The board-relative location of the chip. - */ + /** The board-relative location of the chip. */ private int[] boardChip; - /** - * The physical location of the job. - */ + /** The physical location of the job. */ private int[] physical; /** @@ -70,7 +55,7 @@ public int[] getJobChip() { * @param jobChip * the job chip to set */ - public void setJobChip(final int[] jobChip) { + void setJobChip(int[] jobChip) { this.jobChip = jobChip; } @@ -89,7 +74,7 @@ public int getJobId() { * @param jobId * the job id to set */ - public void setJobId(final int jobId) { + void setJobId(int jobId) { this.jobId = jobId; } @@ -108,7 +93,7 @@ public int[] getChip() { * @param chip * the chip to set */ - public void setChip(final int[] chip) { + void setChip(int[] chip) { this.chip = chip; } @@ -127,7 +112,7 @@ public int[] getLogical() { * @param logical * the logical to set */ - public void setLogical(final int[] logical) { + void setLogical(int[] logical) { this.logical = logical; } @@ -146,7 +131,7 @@ public String getMachine() { * @param machine * the machine to set */ - public void setMachine(final String machine) { + void setMachine(String machine) { this.machine = machine; } @@ -165,7 +150,7 @@ public int[] getBoardChip() { * @param boardChip * the board chip to set */ - public void setBoardChip(final int[] boardChip) { + void setBoardChip(int[] boardChip) { this.boardChip = boardChip; } @@ -184,7 +169,7 @@ public int[] getPhysical() { * @param physical * the physical to set */ - public void setPhysical(final int[] physical) { + void setPhysical(int[] physical) { this.physical = physical; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/APIKeyResponse.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/APIKeyResponse.java index e339995ab9..2a374adef2 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/APIKeyResponse.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/APIKeyResponse.java @@ -37,7 +37,7 @@ public String getKey() { * @param key * The key to set */ - public void setKey(final String key) { + void setKey(String key) { this.key = key; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/Collab.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/Collab.java index 235a45d480..1a11a850d7 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/Collab.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/Collab.java @@ -40,7 +40,7 @@ public String getContent() { * @param content * The value to set. */ - public void setContent(final String content) { + void setContent(String content) { this.content = content; } @@ -59,7 +59,7 @@ public int getId() { * @param id * The collab ID */ - public void setId(final int id) { + void setId(int id) { this.id = id; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/CollabContext.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/CollabContext.java index 43e8fbb249..12650e7936 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/CollabContext.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/CollabContext.java @@ -58,7 +58,7 @@ public String getAppId() { * @param appId * The ID to set */ - public void setAppId(final String appId) { + void setAppId(String appId) { this.appId = appId; } @@ -77,7 +77,7 @@ public Collab getCollab() { * @param collab * The collab to set */ - public void setCollab(final Collab collab) { + void setCollab(Collab collab) { this.collab = collab; } @@ -96,7 +96,7 @@ public String getContext() { * @param context * The context to set */ - public void setContext(final String context) { + void setContext(String context) { this.context = context; } @@ -115,7 +115,7 @@ public int getId() { * @param id * The ID */ - public void setId(final int id) { + void setId(int id) { this.id = id; } @@ -134,7 +134,7 @@ public String getName() { * @param name * The name to set */ - public void setName(final String name) { + void setName(String name) { this.name = name; } @@ -153,7 +153,7 @@ public int getOrderIndex() { * @param orderIndex * The index to set */ - public void setOrderIndex(final int orderIndex) { + void setOrderIndex(int orderIndex) { this.orderIndex = orderIndex; } @@ -172,7 +172,7 @@ public int getParent() { * @param parent * The parent to set */ - public void setParent(final int parent) { + void setParent(int parent) { this.parent = parent; } @@ -191,7 +191,7 @@ public String getType() { * @param type * The type to set */ - public void setType(final String type) { + void setType(String type) { this.type = type; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/CollabPermissions.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/CollabPermissions.java index ed53146443..6ad0a1cd78 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/CollabPermissions.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/CollabPermissions.java @@ -43,7 +43,7 @@ public boolean isDelete() { * @param delete * Whether to give permission */ - public void setDelete(final boolean delete) { + void setDelete(boolean delete) { this.delete = delete; } @@ -63,7 +63,7 @@ public boolean isUpdate() { * @param update * Whether permission is given */ - public void setUpdate(final boolean update) { + void setUpdate(boolean update) { this.update = update; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/DateTimeSerialiser.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/DateTimeSerialiser.java index e5b187c190..548a6c244b 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/DateTimeSerialiser.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/DateTimeSerialiser.java @@ -46,8 +46,8 @@ public DateTimeSerialiser() { * Perform serialisation. */ @Override - public void serialize(final DateTime value, final JsonGenerator jgen, - final SerializerProvider provider) + public void serialize(DateTime value, JsonGenerator jgen, + SerializerProvider provider) throws IOException, JsonGenerationException { jgen.writeString(FORMAT.print(value)); } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/Icinga2CheckResult.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/Icinga2CheckResult.java index 0c981c7c55..f495513d2e 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/Icinga2CheckResult.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/Icinga2CheckResult.java @@ -68,9 +68,8 @@ public class Icinga2CheckResult { * @param service * The service to report on. */ - public Icinga2CheckResult(final int exitStatus, final String pluginOutput, - final String performanceData, final Integer ttl, final String host, - final String service) { + public Icinga2CheckResult(int exitStatus, String pluginOutput, + String performanceData, Integer ttl, String host, String service) { this.exitStatus = exitStatus; this.pluginOutput = pluginOutput; this.performanceData = performanceData; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/NMPILog.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/NMPILog.java index a2a85cfd7a..22e6e4cd9c 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/NMPILog.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/NMPILog.java @@ -51,7 +51,7 @@ public String getContent() { * @param content * The content to set */ - public void setContent(final String content) { + public void setContent(String content) { this.buffer = new StringBuilder(content); } @@ -61,7 +61,7 @@ public void setContent(final String content) { * @param content * The string to append. */ - public void appendContent(final String content) { + public void appendContent(String content) { if (isNull(this.buffer)) { this.buffer = new StringBuilder(content); } else { diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/OutputData.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/OutputData.java index b34faee6bc..8ec98150a1 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/OutputData.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/OutputData.java @@ -42,7 +42,7 @@ public OutputData() { * @param repository * The name of the repository. */ - public OutputData(final String repository) { + public OutputData(String repository) { this.repository = repository; } @@ -61,7 +61,7 @@ public String getRepository() { * @param repository * The repository to set. */ - public void setRepository(String repository) { + void setRepository(String repository) { this.repository = repository; } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueEmpty.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueEmpty.java index 943c8fdc2a..f326a7f209 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueEmpty.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueEmpty.java @@ -18,7 +18,7 @@ /** * A message indicating that the queue is empty. */ -public class QueueEmpty implements QueueNextResponse { +public final class QueueEmpty implements QueueNextResponse { /** Any warning returned. */ private String warning; @@ -37,7 +37,7 @@ public String getWarning() { * @param warning * The warning to set */ - public void setWarning(final String warning) { + void setWarning(String warning) { this.warning = warning; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueJob.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueJob.java index 3cc8aacf33..9ae8862509 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueJob.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueJob.java @@ -20,6 +20,6 @@ /** * A Job that is a response from the queue. */ -public class QueueJob extends Job implements QueueNextResponse { +public final class QueueJob extends Job implements QueueNextResponse { } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueJobCompat.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueJobCompat.java index d8f2232117..03e7de076b 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueJobCompat.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueJobCompat.java @@ -20,7 +20,7 @@ /** * A Job that is a response from the queue. */ -public class QueueJobCompat extends Job implements QueueNextResponse { +public final class QueueJobCompat extends Job implements QueueNextResponse { /** * Sets the collab ID. Wrapper to handle old API. * diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueNextResponse.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueNextResponse.java index db09fbffd0..9a1867fc2b 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueNextResponse.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/model/QueueNextResponse.java @@ -18,6 +18,7 @@ /** * A response from the queue; can end up being one of a number of items. */ -public interface QueueNextResponse { +public sealed interface QueueNextResponse + permits QueueJob, QueueJobCompat, QueueEmpty { // Does Nothing } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManager.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManager.java index 8612a99f53..7f0e892761 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManager.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManager.java @@ -16,7 +16,6 @@ package uk.ac.manchester.spinnaker.nmpi.nmpi; import java.util.List; - import com.fasterxml.jackson.databind.node.ObjectNode; import uk.ac.manchester.spinnaker.nmpi.model.job.nmpi.DataItem; @@ -28,6 +27,7 @@ public interface NMPIQueueManager { /** * Get jobs that are marked as running or in progress in some form. + * * @return A list of jobs. */ List getJobs(); @@ -70,14 +70,14 @@ public interface NMPIQueueManager { * The ID of the job * @param logToAppend * Any additional log messages to append to the existing log - * (null if none) + * ({@code null} if none) * @param outputs * The outputs of the job (null if none) * @param provenance * JSON provenance information */ - void setJobFinished(int id, String logToAppend, - List outputs, ObjectNode provenance); + void setJobFinished(int id, String logToAppend, List outputs, + ObjectNode provenance); /** * Marks a job as finished with an error. @@ -86,7 +86,7 @@ void setJobFinished(int id, String logToAppend, * The ID of the job * @param logToAppend * Any additional log messages to append to the existing log - * (null if none) + * ({@code null} if none) * @param outputs * Any outputs generated, or null if none * @param error @@ -94,9 +94,8 @@ void setJobFinished(int id, String logToAppend, * @param provenance * JSON provenance information */ - void setJobError(int id, String logToAppend, - List outputs, Throwable error, - ObjectNode provenance); + void setJobError(int id, String logToAppend, List outputs, + Throwable error, ObjectNode provenance); /** * Close the manager. diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManagerCompat.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManagerCompat.java index 0fea614cec..6cd0ddf98e 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManagerCompat.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManagerCompat.java @@ -16,6 +16,7 @@ package uk.ac.manchester.spinnaker.nmpi.nmpi; import static java.util.Objects.nonNull; +import static java.util.stream.Collectors.toList; import static org.joda.time.DateTimeZone.UTC; import static org.slf4j.LoggerFactory.getLogger; import static uk.ac.manchester.spinnaker.utils.ThreadUtils.sleep; @@ -29,12 +30,11 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.PostConstruct; -import javax.ws.rs.NotFoundException; -import javax.ws.rs.WebApplicationException; +import jakarta.annotation.PostConstruct; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.WebApplicationException; import org.joda.time.DateTime; import org.slf4j.Logger; @@ -48,6 +48,7 @@ import uk.ac.manchester.spinnaker.nmpi.model.QueueEmpty; import uk.ac.manchester.spinnaker.nmpi.model.QueueNextResponse; import uk.ac.manchester.spinnaker.nmpi.rest.JobDoneCompat; +import uk.ac.manchester.spinnaker.nmpi.rest.JobListCompat; import uk.ac.manchester.spinnaker.nmpi.rest.JobStatusOnlyCompat; import uk.ac.manchester.spinnaker.nmpi.rest.NMPIQueueCompat; @@ -74,7 +75,7 @@ public class NMPIQueueManagerCompat implements NMPIQueueManager { private static final String NMPI_AUTH = "ApiKey"; /** The queue to get jobs from. */ - private NMPIQueueCompat queue; + private Queue queue; /** Marker to indicate if the manager is done or not. */ private boolean done = false; @@ -107,40 +108,75 @@ public class NMPIQueueManagerCompat implements NMPIQueueManager { @Value("${nmpi.username}") private String nmpiUsername; - /** The authorization header to use. **/ - private String nmpiAuthHeader; + /** + * Wraps the constant values associated with the NMPI queue API. + */ + private static final class Queue { + /** The queue to get jobs from. */ + private final NMPIQueueCompat queue; + + /** The authorization header to use. **/ + private final String authHeader; + + /** The hardware identifier for the queue. */ + private final String hardware; + + Queue(URL url, String username, String apiKey, String hardware) { + queue = NMPIQueueCompat.createClient(url.toString()); + authHeader = NMPI_AUTH + " " + username + ":" + apiKey; + this.hardware = hardware; + } + + JobListCompat getJobs(String status) { + return queue.getJobs(authHeader, hardware, status); + } + + QueueNextResponse getNextJob() { + return queue.getNextJob(authHeader, hardware); + } + + void updateJobStatus(int jobId, String status) { + queue.updateJobStatus(authHeader, jobId, + new JobStatusOnlyCompat(jobId, status)); + } + + void updateJobLog(int jobId, StringBuilder log) { + queue.updateJobLog(authHeader, jobId, new NMPILog(log)); + } + + void finishJob(int jobId, String status, List outputs, + ObjectNode provenance) { + var jobDone = new JobDoneCompat(jobId, status); + jobDone.setOutputData(outputs); + jobDone.setProvenance(provenance); + jobDone.setTimestampCompletion(new DateTime(UTC)); + queue.finishJob(authHeader, jobId, jobDone); + } + } /** * Initialise the client. */ @PostConstruct private void initAPIClient() { - queue = NMPIQueueCompat.createClient(nmpiUrl.toString()); - nmpiAuthHeader = NMPI_AUTH + " " + nmpiUsername + ":" + nmpiApiKey; + queue = new Queue(nmpiUrl, nmpiUsername, nmpiApiKey, hardware); } @Override public List getJobs() { - var val = queue.getJobs(nmpiAuthHeader, hardware, STATUS_VALIDATED); - var run = queue.getJobs(nmpiAuthHeader, hardware, STATUS_RUNNING); + var val = queue.getJobs(STATUS_VALIDATED); + var run = queue.getJobs(STATUS_RUNNING); return Stream .concat(val.getObjects().stream(), run.getObjects().stream()) - .collect(Collectors.toList()); + .collect(toList()); } - /** - * Register a listener against the manager for new jobs. - * - * @param listener - * The listener to register - */ @Override - public void addListener(final NMPIQueueListener listener) { + public void addListener(NMPIQueueListener listener) { listeners.add(listener); } - private void handleWebAppError(final WebApplicationException e, - final String action) { + private void handleWebAppError(WebApplicationException e, String action) { var body = e.getResponse().readEntity(String.class); logger.error("Error {} ({}), continuing: {}", action, e.getMessage(), body); @@ -153,15 +189,15 @@ public void processResponsesFromQueue() { logger.debug("Getting next job"); QueueNextResponse response; try { - response = queue.getNextJob(nmpiAuthHeader, hardware); - } catch (final NotFoundException e) { + response = queue.getNextJob(); + } catch (NotFoundException e) { response = new QueueEmpty(); } processResponse(response); - } catch (final WebApplicationException e) { + } catch (WebApplicationException e) { handleWebAppError(e, "getting next job"); sleep(EMPTY_QUEUE_SLEEP_MS); - } catch (final Exception e) { + } catch (Exception e) { logger.error("Error in getting next job", e); sleep(EMPTY_QUEUE_SLEEP_MS); } @@ -174,11 +210,11 @@ public void processResponsesFromQueue() { * @param response * The response to process */ - private void processResponse(final QueueNextResponse response) { + private void processResponse(QueueNextResponse response) { if (response instanceof QueueEmpty) { sleep(EMPTY_QUEUE_SLEEP_MS); - } else if (response instanceof Job) { - processResponse((Job) response); + } else if (response instanceof Job j) { + processResponse(j); } else { throw new IllegalStateException(); } @@ -190,98 +226,63 @@ private void processResponse(final QueueNextResponse response) { * @param job * The job to process */ - private void processResponse(final Job job) { + private void processResponse(Job job) { synchronized (jobCache) { jobCache.put(job.getId(), job); } logger.debug("Job {} received", job.getId()); try { - for (final var listener : listeners) { + for (var listener : listeners) { listener.addJob(job); } logger.debug("Setting job status"); logger.debug("Updating job status on server"); - queue.updateJobStatus(nmpiAuthHeader, job.getId(), - new JobStatusOnlyCompat(job.getId(), STATUS_VALIDATED)); - } catch (final WebApplicationException e) { + queue.updateJobStatus(job.getId(), STATUS_VALIDATED); + } catch (WebApplicationException e) { handleWebAppError(e, "updating job"); setJobError(job.getId(), null, null, e, null); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error in updating job", e); setJobError(job.getId(), null, null, e, null); } } - /** - * Appends log messages to the log. - * - * @param id - * The ID of the job - * @param logToAppend - * The messages to append - */ @Override - public void appendJobLog(final int id, final String logToAppend) { - var existingLog = jobLog.computeIfAbsent( - id, ignored -> new StringBuilder()); + public void appendJobLog(int id, String logToAppend) { + var existingLog = + jobLog.computeIfAbsent(id, ignored -> new StringBuilder()); existingLog.append(logToAppend); logger.debug("Job {} log is being updated", id); try { - queue.updateJobLog(nmpiAuthHeader, id, - new NMPILog(existingLog)); + queue.updateJobLog(id, existingLog); } catch (WebApplicationException e) { handleWebAppError(e, "updating job log"); } } - /** - * Mark a job as running. - * - * @param id - * The ID of the job. - */ @Override - public void setJobRunning(final int id) { + public void setJobRunning(int id) { logger.debug("Job {} is running", id); logger.debug("Updating job status on server"); try { - queue.updateJobStatus(nmpiAuthHeader, id, - new JobStatusOnlyCompat(id, STATUS_RUNNING)); + queue.updateJobStatus(id, STATUS_RUNNING); } catch (WebApplicationException e) { handleWebAppError(e, "setting job to running"); } } - /** - * Marks a job as finished successfully. - * - * @param id - * The ID of the job - * @param logToAppend - * Any additional log messages to append to the existing log - * (null if none) - * @param outputs - * The outputs of the job (null if none) - * @param provenance - * JSON provenance information - */ @Override - public void setJobFinished(final int id, final String logToAppend, - final List outputs, final ObjectNode provenance) { + public void setJobFinished(int id, String logToAppend, + List outputs, ObjectNode provenance) { logger.debug("Job {} is finished", id); if (nonNull(logToAppend)) { appendJobLog(id, logToAppend); } - final var job = new JobDoneCompat(id, STATUS_FINISHED); - job.setOutputData(outputs); - job.setTimestampCompletion(new DateTime(UTC)); - job.setProvenance(provenance); - try { logger.debug("Updating job status on server"); - queue.finishJob(nmpiAuthHeader, id, job); + queue.finishJob(id, STATUS_FINISHED, outputs, provenance); } catch (WebApplicationException e) { handleWebAppError(e, "finishing job"); } @@ -289,29 +290,13 @@ public void setJobFinished(final int id, final String logToAppend, jobCache.remove(id); } - /** - * Marks a job as finished with an error. - * - * @param id - * The ID of the job - * @param logToAppend - * Any additional log messages to append to the existing log - * (null if none) - * @param outputs - * Any outputs generated, or null if none - * @param error - * The error details - * @param provenance - * JSON provenance information - */ @Override - public void setJobError(final int id, final String logToAppend, - final List outputs, final Throwable error, - final ObjectNode provenance) { + public void setJobError(int id, String logToAppend, List outputs, + Throwable error, ObjectNode provenance) { logger.debug("Job {} finished with an error", id); - final var errors = new StringWriter(); + var errors = new StringWriter(); error.printStackTrace(new PrintWriter(errors)); - final var logMessage = new StringBuilder(); + var logMessage = new StringBuilder(); if (nonNull(logToAppend)) { logMessage.append(logToAppend); } @@ -322,14 +307,9 @@ public void setJobError(final int id, final String logToAppend, logMessage.append(errors.toString()); appendJobLog(id, logMessage.toString()); - final var job = new JobDoneCompat(id, STATUS_ERROR); - job.setTimestampCompletion(new DateTime(UTC)); - job.setOutputData(outputs); - job.setProvenance(provenance); - try { logger.debug("Updating job on server"); - queue.finishJob(nmpiAuthHeader, id, job); + queue.finishJob(id, STATUS_ERROR, outputs, provenance); } catch (WebApplicationException e) { handleWebAppError(e, "finishing job on error"); } @@ -338,9 +318,6 @@ public void setJobError(final int id, final String logToAppend, jobCache.remove(id); } - /** - * Close the manager. - */ @Override public void close() { done = true; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManagerV3.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManagerV3.java index 00e8bac556..e051f5ced0 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManagerV3.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/nmpi/NMPIQueueManagerV3.java @@ -30,9 +30,9 @@ import java.util.Map; import java.util.Set; -import javax.annotation.PostConstruct; -import javax.ws.rs.NotFoundException; -import javax.ws.rs.WebApplicationException; +import jakarta.annotation.PostConstruct; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.WebApplicationException; import org.joda.time.DateTime; import org.slf4j.Logger; @@ -73,9 +73,6 @@ public class NMPIQueueManagerV3 implements NMPIQueueManager { /** The amount of time to sleep when an empty queue is detected. */ private static final int EMPTY_QUEUE_SLEEP_MS = 10000; - /** The queue to get jobs from. */ - private NMPIQueue queue; - /** Marker to indicate if the manager is done or not. */ private boolean done = false; @@ -103,33 +100,77 @@ public class NMPIQueueManagerV3 implements NMPIQueueManager { @Value("${nmpi.apiKey}") private String nmpiApiKey; + private Queue queue; + + /** + * Wraps the constant values associated with the NMPI queue API. + */ + private static final class Queue { + /** The queue to get jobs from. */ + private final NMPIQueue queue; + + /** The API key to authenticate against the server. */ + private final String apiKey; + + /** The hardware identifier for the queue. */ + private final String hardware; + + Queue(URL nmpiUrl, String nmpiApiKey, String hardware) { + queue = NMPIQueue.createClient(nmpiUrl.toString()); + apiKey = nmpiApiKey; + this.hardware = hardware; + } + + List getJobs(List statuses) { + return queue.getJobs(apiKey, hardware, statuses); + } + + QueueNextResponse getNextJob() { + return queue.getNextJob(apiKey, hardware); + } + + void updateJobStatus(int jobId, String status) { + queue.updateJobStatus(apiKey, jobId, new JobStatusOnly(status)); + } + + void updateJobLog(int jobId, StringBuilder log) { + queue.updateJobLog(apiKey, jobId, + new JobLogOnly(log.toString())); + } + + void finishJob(int jobId, String status, List outputs, + ObjectNode provenance) { + var outputData = new OutputData(REPOSITORY); + outputData.setFiles(outputs); + + var jobDone = new JobDone(status); + jobDone.setTimestampCompletion(new DateTime(UTC)); + jobDone.setOutputData(outputData); + jobDone.setProvenance(provenance); + + queue.finishJob(apiKey, jobId, jobDone); + } + } + /** * Initialise the client. */ @PostConstruct private void initAPIClient() { - queue = NMPIQueue.createClient(nmpiUrl.toString()); + queue = new Queue(nmpiUrl, nmpiApiKey, hardware); } @Override public List getJobs() { - return queue.getJobs(nmpiApiKey, hardware, - List.of(STATUS_VALIDATED, STATUS_RUNNING)); + return queue.getJobs(List.of(STATUS_VALIDATED, STATUS_RUNNING)); } - /** - * Register a listener against the manager for new jobs. - * - * @param listener - * The listener to register - */ @Override - public void addListener(final NMPIQueueListener listener) { + public void addListener(NMPIQueueListener listener) { listeners.add(listener); } - private void handleWebAppError(final WebApplicationException e, - final String action) { + private void handleWebAppError(WebApplicationException e, String action) { var body = e.getResponse().readEntity(String.class); logger.error("Error {} ({}), continuing: {}", action, e.getMessage(), body); @@ -142,15 +183,15 @@ public void processResponsesFromQueue() { logger.debug("Getting next job"); QueueNextResponse response; try { - response = queue.getNextJob(nmpiApiKey, hardware); - } catch (final NotFoundException e) { + response = queue.getNextJob(); + } catch (NotFoundException e) { response = new QueueEmpty(); } processResponse(response); - } catch (final WebApplicationException e) { + } catch (WebApplicationException e) { handleWebAppError(e, "getting next job"); sleep(EMPTY_QUEUE_SLEEP_MS); - } catch (final Exception e) { + } catch (Exception e) { logger.error("Error in getting next job", e); sleep(EMPTY_QUEUE_SLEEP_MS); } @@ -163,11 +204,11 @@ public void processResponsesFromQueue() { * @param response * The response to process */ - private void processResponse(final QueueNextResponse response) { + private void processResponse(QueueNextResponse response) { if (response instanceof QueueEmpty) { sleep(EMPTY_QUEUE_SLEEP_MS); - } else if (response instanceof Job) { - processResponse((Job) response); + } else if (response instanceof Job job) { + processResponse(job); } else { throw new IllegalStateException(); } @@ -179,100 +220,63 @@ private void processResponse(final QueueNextResponse response) { * @param job * The job to process */ - private void processResponse(final Job job) { + private void processResponse(Job job) { synchronized (jobCache) { jobCache.put(job.getId(), job); } logger.debug("Job {} received", job.getId()); try { - for (final var listener : listeners) { + for (var listener : listeners) { listener.addJob(job); } logger.debug("Setting job status"); logger.debug("Updating job status on server"); - queue.updateJobStatus(nmpiApiKey, job.getId(), - new JobStatusOnly(STATUS_VALIDATED)); - } catch (final WebApplicationException e) { + queue.updateJobStatus(job.getId(), STATUS_VALIDATED); + } catch (WebApplicationException e) { handleWebAppError(e, "updating job"); setJobError(job.getId(), null, null, e, null); - } catch (final IOException e) { + } catch (IOException e) { logger.error("Error in updating job", e); setJobError(job.getId(), null, null, e, null); } } - /** - * Appends log messages to the log. - * - * @param id - * The ID of the job - * @param logToAppend - * The messages to append - */ @Override - public void appendJobLog(final int id, final String logToAppend) { + public void appendJobLog(int id, String logToAppend) { var existingLog = jobLog.computeIfAbsent( id, ignored -> new StringBuilder()); existingLog.append(logToAppend); logger.debug("Job {} log is being updated", id); try { - queue.updateJobLog(nmpiApiKey, id, - new JobLogOnly(existingLog.toString())); + queue.updateJobLog(id, existingLog); } catch (WebApplicationException e) { handleWebAppError(e, "updating job log"); } } - /** - * Mark a job as running. - * - * @param id - * The ID of the job. - */ @Override - public void setJobRunning(final int id) { + public void setJobRunning(int id) { logger.debug("Job {} is running", id); logger.debug("Updating job status on server"); try { - queue.updateJobStatus(nmpiApiKey, id, - new JobStatusOnly(STATUS_RUNNING)); + queue.updateJobStatus(id, STATUS_RUNNING); } catch (WebApplicationException e) { handleWebAppError(e, "setting job to running"); } } - /** - * Marks a job as finished successfully. - * - * @param id - * The ID of the job - * @param logToAppend - * Any additional log messages to append to the existing log - * (null if none) - * @param outputs - * The outputs of the job (null if none) - * @param provenance - * JSON provenance information - */ @Override - public void setJobFinished(final int id, final String logToAppend, - final List outputs, final ObjectNode provenance) { + public void setJobFinished(int id, String logToAppend, + List outputs, ObjectNode provenance) { logger.debug("Job {} is finished", id); if (nonNull(logToAppend)) { appendJobLog(id, logToAppend); } - final var outputData = new OutputData(REPOSITORY); - final var job = new JobDone(STATUS_FINISHED); - outputData.setFiles(outputs); - job.setOutputData(outputData); - job.setTimestampCompletion(new DateTime(UTC)); - job.setProvenance(provenance); - try { logger.debug("Updating job status on server"); - queue.finishJob(nmpiApiKey, id, job); + queue.finishJob(id, STATUS_FINISHED, outputs, provenance); } catch (WebApplicationException e) { handleWebAppError(e, "finishing job"); } @@ -280,29 +284,13 @@ public void setJobFinished(final int id, final String logToAppend, jobCache.remove(id); } - /** - * Marks a job as finished with an error. - * - * @param id - * The ID of the job - * @param logToAppend - * Any additional log messages to append to the existing log - * (null if none) - * @param outputs - * Any outputs generated, or null if none - * @param error - * The error details - * @param provenance - * JSON provenance information - */ @Override - public void setJobError(final int id, final String logToAppend, - final List outputs, final Throwable error, - final ObjectNode provenance) { + public void setJobError(int id, String logToAppend, List outputs, + Throwable error, ObjectNode provenance) { logger.debug("Job {} finished with an error", id); - final var errors = new StringWriter(); + var errors = new StringWriter(); error.printStackTrace(new PrintWriter(errors)); - final var logMessage = new StringBuilder(); + var logMessage = new StringBuilder(); if (nonNull(logToAppend)) { logMessage.append(logToAppend); } @@ -313,16 +301,9 @@ public void setJobError(final int id, final String logToAppend, logMessage.append(errors.toString()); appendJobLog(id, logMessage.toString()); - final var job = new JobDone(STATUS_ERROR); - final var outputData = new OutputData(REPOSITORY); - outputData.setFiles(outputs); - job.setTimestampCompletion(new DateTime(UTC)); - job.setOutputData(outputData); - job.setProvenance(provenance); - try { logger.debug("Updating job on server"); - queue.finishJob(nmpiApiKey, id, job); + queue.finishJob(id, STATUS_ERROR, outputs, provenance); } catch (WebApplicationException e) { handleWebAppError(e, "finishing job on error"); } @@ -331,9 +312,6 @@ public void setJobError(final int id, final String logToAppend, jobCache.remove(id); } - /** - * Close the manager. - */ @Override public void close() { done = true; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/CollabRestService.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/CollabRestService.java index fe8293cbfe..ee9a65f4eb 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/CollabRestService.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/CollabRestService.java @@ -15,12 +15,12 @@ */ package uk.ac.manchester.spinnaker.nmpi.rest; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; import uk.ac.manchester.spinnaker.nmpi.model.CollabContext; import uk.ac.manchester.spinnaker.nmpi.model.CollabPermissions; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerAPI.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerAPI.java index e1af6eb2bc..778c3c5af4 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerAPI.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerAPI.java @@ -15,7 +15,7 @@ */ package uk.ac.manchester.spinnaker.nmpi.rest; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import java.io.ByteArrayInputStream; import java.io.DataInputStream; @@ -23,19 +23,19 @@ import java.io.IOException; import java.util.List; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -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 jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; /** * Interface to the Docker API. @@ -179,6 +179,7 @@ static String readLog(byte[] data) throws IOException { int size = input.readInt(); var text = new byte[size]; input.readFully(text); + // TODO what encoding? output.append(new String(text)); } } catch (EOFException e) { diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerCreateRequest.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerCreateRequest.java index 21cae2c83b..8bbe60ae08 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerCreateRequest.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerCreateRequest.java @@ -64,7 +64,7 @@ public List getCmd() { /** * @param cmd - * the cmd to set + * the command to set */ public void setCmd(List cmd) { this.cmd = cmd; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerCreateResponse.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerCreateResponse.java index 21ec188ed7..61c24eaac4 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerCreateResponse.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/DockerCreateResponse.java @@ -28,7 +28,7 @@ public class DockerCreateResponse { private List warnings; /** - * @return the id + * @return the ID */ public String getId() { return id; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/Icinga2.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/Icinga2.java index 89cf0f361c..2d83cf1708 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/Icinga2.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/Icinga2.java @@ -16,21 +16,21 @@ package uk.ac.manchester.spinnaker.nmpi.rest; import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; import java.util.List; import java.util.Map; -import javax.ws.rs.Consumes; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import uk.ac.manchester.spinnaker.nmpi.model.Icinga2CheckResult; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/JobDone.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/JobDone.java index 9d21aea90e..20db068683 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/JobDone.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/JobDone.java @@ -100,7 +100,7 @@ public OutputData getOutputData() { * @param outputData * the outputData to set */ - public void setOutputData(final OutputData outputData) { + public void setOutputData(OutputData outputData) { this.outputData = outputData; } @@ -119,7 +119,7 @@ public ObjectNode getProvenance() { * @param provenance * the provenance to set */ - public void setProvenance(final ObjectNode provenance) { + public void setProvenance(ObjectNode provenance) { this.provenance = provenance; } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/JobDoneCompat.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/JobDoneCompat.java index 3923417097..e4328dcb10 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/JobDoneCompat.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/JobDoneCompat.java @@ -87,13 +87,12 @@ public DateTime getTimestampCompletion() { * @param timestampCompletion * the timestampCompletion to set */ - public void setTimestampCompletion( - final DateTime timestampCompletion) { + public void setTimestampCompletion(DateTime timestampCompletion) { this.timestampCompletion = timestampCompletion; } /** - * Get the outputData. + * Get the output data items. * * @return the outputData */ @@ -107,7 +106,7 @@ public List getOutputData() { * @param outputData * the outputData to set */ - public void setOutputData(final List outputData) { + public void setOutputData(List outputData) { this.outputData = outputData; } @@ -126,7 +125,7 @@ public ObjectNode getProvenance() { * @param provenance * the provenance to set */ - public void setProvenance(final ObjectNode provenance) { + public void setProvenance(ObjectNode provenance) { this.provenance = provenance; } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/NMPIQueue.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/NMPIQueue.java index 76645c63a9..fe01f241ea 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/NMPIQueue.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/NMPIQueue.java @@ -19,19 +19,19 @@ import java.util.List; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import uk.ac.manchester.spinnaker.nmpi.model.QueueEmpty; import uk.ac.manchester.spinnaker.nmpi.model.QueueJob; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/NMPIQueueCompat.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/NMPIQueueCompat.java index 78f9d97ebc..f41ce33808 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/NMPIQueueCompat.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/NMPIQueueCompat.java @@ -19,19 +19,19 @@ import java.util.List; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import uk.ac.manchester.spinnaker.nmpi.model.NMPILog; import uk.ac.manchester.spinnaker.nmpi.model.QueueEmpty; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/OutputManager.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/OutputManager.java index d69fce0e53..d809f4db42 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/OutputManager.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/OutputManager.java @@ -15,22 +15,22 @@ */ package uk.ac.manchester.spinnaker.nmpi.rest; -import static javax.ws.rs.core.MediaType.MEDIA_TYPE_WILDCARD; -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; +import static jakarta.ws.rs.core.MediaType.MEDIA_TYPE_WILDCARD; +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.List; -import javax.ws.rs.DefaultValue; -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.Response; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Response; import uk.ac.manchester.spinnaker.nmpi.model.job.nmpi.DataItem; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/StatusCake.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/StatusCake.java index 5b72d1bcfa..6462e622a4 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/StatusCake.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/StatusCake.java @@ -19,14 +19,14 @@ import java.util.List; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; /** * Interface to StatusCake API. diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/UnicoreFileClient.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/UnicoreFileClient.java index 4266d361c0..7fa159d29d 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/UnicoreFileClient.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/UnicoreFileClient.java @@ -20,17 +20,17 @@ import java.io.InputStream; import java.util.List; -import javax.ws.rs.Consumes; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.WebApplicationException; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.WebApplicationException; import org.apache.cxf.jaxrs.client.JAXRSClientFactory; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; /** * An interface to the UNICORE storage REST API. diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/CustomJacksonJsonProvider.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/CustomJacksonJsonProvider.java index aede499fca..9fdce169b2 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/CustomJacksonJsonProvider.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/CustomJacksonJsonProvider.java @@ -17,7 +17,7 @@ import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE; -import static javax.ws.rs.core.MediaType.WILDCARD; +import static jakarta.ws.rs.core.MediaType.WILDCARD; import java.io.IOException; import java.io.InputStream; @@ -27,17 +27,17 @@ import java.util.HashSet; import java.util.Set; -import javax.ws.rs.Consumes; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.ext.Provider; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.datatype.joda.JodaModule; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; /** * Extended JSON serialisation handler. @@ -65,8 +65,8 @@ public class CustomJacksonJsonProvider extends JacksonJsonProvider { * @param deserialiser * The deserialiser. */ - public void addDeserialiser(final Class type, - final StdDeserializer deserialiser) { + public void addDeserialiser(Class type, + StdDeserializer deserialiser) { module.addDeserializer(type, deserialiser); } @@ -78,9 +78,8 @@ public void addDeserialiser(final Class type, * @param mediaType * The media type to handle */ - private void registerMapper(final Class type, - final MediaType mediaType) { - final var mapper = locateMapper(type, mediaType); + private void registerMapper(Class type, MediaType mediaType) { + var mapper = locateMapper(type, mediaType); if (!registeredMappers.contains(mapper)) { mapper.registerModule(module); mapper.setPropertyNamingStrategy(SNAKE_CASE); @@ -91,21 +90,20 @@ private void registerMapper(final Class type, } @Override - public Object readFrom(final Class type, final Type genericType, - final Annotation[] annotations, final MediaType mediaType, - final MultivaluedMap httpHeaders, - final InputStream entityStream) throws IOException { + public Object readFrom(Class type, Type genericType, + Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, + InputStream entityStream) throws IOException { registerMapper(type, mediaType); return super.readFrom(type, genericType, annotations, mediaType, httpHeaders, entityStream); } @Override - public void writeTo(final Object value, final Class type, - final Type genericType, final Annotation[] annotations, - final MediaType mediaType, - final MultivaluedMap httpHeaders, - final OutputStream entityStream) throws IOException { + public void writeTo(Object value, Class type, Type genericType, + Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, + OutputStream entityStream) throws IOException { registerMapper(type, mediaType); super.writeTo(value, type, genericType, annotations, mediaType, httpHeaders, entityStream); diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/ErrorCaptureResponseFilter.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/ErrorCaptureResponseFilter.java index 29ad484e8a..79ef49b761 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/ErrorCaptureResponseFilter.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/ErrorCaptureResponseFilter.java @@ -17,17 +17,17 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.nonNull; -import static javax.ws.rs.core.Response.Status.Family.CLIENT_ERROR; -import static javax.ws.rs.core.Response.Status.Family.SERVER_ERROR; +import static jakarta.ws.rs.core.Response.Status.Family.CLIENT_ERROR; +import static jakarta.ws.rs.core.Response.Status.Family.SERVER_ERROR; import static org.slf4j.LoggerFactory.getLogger; import java.io.IOException; import java.io.StringWriter; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientResponseContext; -import javax.ws.rs.client.ClientResponseFilter; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.client.ClientRequestContext; +import jakarta.ws.rs.client.ClientResponseContext; +import jakarta.ws.rs.client.ClientResponseFilter; +import jakarta.ws.rs.ext.Provider; import org.apache.commons.io.output.WriterOutputStream; import org.slf4j.Logger; @@ -56,18 +56,18 @@ public class ErrorCaptureResponseFilter implements ClientResponseFilter { private static final String IND2 = INDENT + INDENT; @Override - public void filter(final ClientRequestContext requestContext, - final ClientResponseContext responseContext) throws IOException { + public void filter(ClientRequestContext requestContext, + ClientResponseContext responseContext) throws IOException { if (!writeToLog) { return; } - final var family = responseContext.getStatusInfo().getFamily(); + var family = responseContext.getStatusInfo().getFamily(); if ((family == CLIENT_ERROR) || (family == SERVER_ERROR)) { logger.trace("Error when sending request:"); logger.trace(INDENT + "Headers:"); - final var headers = requestContext.getStringHeaders(); - for (final var headerName : headers.keySet()) { - for (final var headerValue : headers.get(headerName)) { + var headers = requestContext.getStringHeaders(); + for (var headerName : headers.keySet()) { + for (var headerValue : headers.get(headerName)) { logger.trace(IND2 + "{}: {}", headerName, headerValue); } } @@ -75,7 +75,7 @@ public void filter(final ClientRequestContext requestContext, logger.trace(INDENT + "Entity:"); logger.trace(IND2 + "{}", requestContext.getEntity()); - final var json = getRequestAsJSON(requestContext); + var json = getRequestAsJSON(requestContext); if (nonNull(json)) { logger.trace(INDENT + "JSON version:"); logger.trace(IND2 + "{}", json); @@ -90,10 +90,11 @@ public void filter(final ClientRequestContext requestContext, * The context of the request * @return A JSON String */ - private String getRequestAsJSON(final ClientRequestContext requestContext) { + private String getRequestAsJSON(ClientRequestContext requestContext) { try { - final var jsonWriter = new StringWriter(); - try (var jsonOutput = new WriterOutputStream(jsonWriter, UTF_8)) { + var jsonWriter = new StringWriter(); + try (var jsonOutput = WriterOutputStream.builder().setCharset(UTF_8) + .setWriter(jsonWriter).get()) { provider.writeTo(requestContext.getEntity(), requestContext.getEntityClass(), requestContext.getEntityType(), @@ -102,7 +103,7 @@ private String getRequestAsJSON(final ClientRequestContext requestContext) { requestContext.getHeaders(), jsonOutput); } return jsonWriter.toString(); - } catch (final Exception e) { + } catch (Exception e) { logger.trace("problem when converting request to JSON", e); return null; } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/NullExceptionMapper.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/NullExceptionMapper.java index 376cd3d5f6..6d749ac375 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/NullExceptionMapper.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/NullExceptionMapper.java @@ -16,13 +16,13 @@ package uk.ac.manchester.spinnaker.nmpi.rest.utils; import static java.util.Objects.isNull; -import static javax.ws.rs.core.Response.status; -import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static jakarta.ws.rs.core.Response.status; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static org.slf4j.LoggerFactory.getLogger; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import org.slf4j.Logger; @@ -38,7 +38,7 @@ public class NullExceptionMapper private static final Logger logger = getLogger(NullExceptionMapper.class); @Override - public Response toResponse(final NullPointerException exception) { + public Response toResponse(NullPointerException exception) { var msg = exception.getMessage(); if (isNull(msg) || msg.isEmpty()) { msg = "bad parameter"; diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/PropertyBasedDeserialiser.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/PropertyBasedDeserialiser.java index 18a6889e59..3df99cce5e 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/PropertyBasedDeserialiser.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/rest/utils/PropertyBasedDeserialiser.java @@ -38,9 +38,7 @@ public class PropertyBasedDeserialiser extends StdDeserializer { private static final long serialVersionUID = 1L; - /** - * The registry of known elements. - */ + /** The registry of known elements. */ private final Map> registry = new HashMap<>(); /** @@ -49,7 +47,7 @@ public class PropertyBasedDeserialiser extends StdDeserializer { * @param type * The (super)class of the values that will be produced. */ - public PropertyBasedDeserialiser(final Class type) { + public PropertyBasedDeserialiser(Class type) { super(type); } @@ -64,8 +62,7 @@ public PropertyBasedDeserialiser(final Class type) { * @throws IllegalArgumentException * if one of the parameters is null. */ - public void register(final String propertyName, - final Class type) { + public void register(String propertyName, Class type) { if (isNull(propertyName)) { throw new IllegalArgumentException("propertyName must be non-null"); } @@ -77,11 +74,10 @@ public void register(final String propertyName, } @Override - public T deserialize(final JsonParser parser, - final DeserializationContext context) + public T deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException { - final var root = parser.readValueAsTree(); - final var elementsIterator = root.fieldNames(); + var root = parser.readValueAsTree(); + var elementsIterator = root.fieldNames(); while (elementsIterator.hasNext()) { var c = registry.get(elementsIterator.next()); if (nonNull(c)) { diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/Icinga2StatusMonitorManagerImpl.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/Icinga2StatusMonitorManagerImpl.java index 525c4e5303..d8b945f7dd 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/Icinga2StatusMonitorManagerImpl.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/Icinga2StatusMonitorManagerImpl.java @@ -20,8 +20,8 @@ import java.net.URL; -import javax.annotation.PostConstruct; -import javax.ws.rs.WebApplicationException; +import jakarta.annotation.PostConstruct; +import jakarta.ws.rs.WebApplicationException; import org.apache.ws.commons.util.Base64; import org.slf4j.Logger; @@ -86,13 +86,13 @@ private void init() { } @Override - public void updateStatus(final int runningJobs, final int nBoardsInUse) { - final var performanceData = "'Running Jobs'=" + runningJobs + public void updateStatus(int runningJobs, int nBoardsInUse) { + var performanceData = "'Running Jobs'=" + runningJobs + " 'Boards In Use'=" + nBoardsInUse; - final int ttl = JobManager.STATUS_UPDATE_PERIOD - * STATUS_UPDATE_TTL_MULTIPLIER; - final var result = new Icinga2CheckResult( - STATUS, STATUS_MESSAGE, performanceData, ttl, host, service); + int ttl = + JobManager.STATUS_UPDATE_PERIOD * STATUS_UPDATE_TTL_MULTIPLIER; + var result = new Icinga2CheckResult(STATUS, STATUS_MESSAGE, + performanceData, ttl, host, service); try { var response = icinga.processCheckResult(authHeader, result); logger.debug("Status updated, result = {}", response); diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/NullStatusMonitorManagerImpl.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/NullStatusMonitorManagerImpl.java index 63b83a3714..0ce0ece0c5 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/NullStatusMonitorManagerImpl.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/NullStatusMonitorManagerImpl.java @@ -20,7 +20,7 @@ */ public class NullStatusMonitorManagerImpl implements StatusMonitorManager { @Override - public void updateStatus(final int runningJobs, final int nBoardsInUse) { + public void updateStatus(int runningJobs, int nBoardsInUse) { // Do Nothing } } diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/StatusCakeStatusMonitorManagerImpl.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/StatusCakeStatusMonitorManagerImpl.java index 4c6f1526b2..4e04b96b99 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/StatusCakeStatusMonitorManagerImpl.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/status/StatusCakeStatusMonitorManagerImpl.java @@ -17,7 +17,7 @@ import static org.slf4j.LoggerFactory.getLogger; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; @@ -56,7 +56,7 @@ private void init() { } @Override - public void updateStatus(final int runningJobs, final int nBoardsInUse) { + public void updateStatus(int runningJobs, int nBoardsInUse) { logger.debug( "Updating to Status Cake - runningJobs = {}, nBoardsInUse = {}", runningJobs, nBoardsInUse); diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/web/RemoteSpinnakerBeans.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/web/RemoteSpinnakerBeans.java index c44024e3f7..c7191a17e7 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/web/RemoteSpinnakerBeans.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/web/RemoteSpinnakerBeans.java @@ -34,7 +34,7 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.convert.converter.Converter; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; import uk.ac.manchester.spinnaker.nmpi.jobmanager.DockerExecutorFactory; import uk.ac.manchester.spinnaker.nmpi.jobmanager.JobExecuterFactory; @@ -70,13 +70,9 @@ public class RemoteSpinnakerBeans { * Types of status possible. */ public enum StatusServiceType { - /** - * Status Cake service. - */ + /** Status Cake service. */ STATUS_CAKE, - /** - * Icigna2 service. - */ + /** Icigna2 service. */ ICINGA2 } @@ -98,81 +94,61 @@ public enum StatusServiceType { */ @Bean public static ConversionServiceFactoryBean conversionService() { - final var factory = new ConversionServiceFactoryBean(); + var factory = new ConversionServiceFactoryBean(); factory.setConverters(singleton((StringToMachineConverter) SpinnakerMachine::parse)); return factory; } + @FunctionalInterface interface StringToMachineConverter extends Converter { // Marker interface to allow lambda } - /** - * The context of the application. - */ + /** The context of the application. */ @Autowired private ApplicationContext ctx; - /** - * Determine if machines are to be spalloc allocated. - */ + /** Determine if machines are to be allocated by spalloc. */ @Value("${spalloc.enabled}") private boolean useSpalloc; - /** * Whether we should use the Java or original Spalloc implementation. */ @Value("${spalloc.use_java}") private boolean spallocUseJava; - /** - * Determine if local jobs or Xen VMs are to be used. - */ + /** Determine if local jobs or Xen VMs are to be used. */ @Value("${xen.server.enabled}") private boolean useXenVms; - /** - * Determine whether to use docker instead. - */ + /** Determine whether to use docker instead. */ @Value("${docker.enabled}") private boolean useDocker; - /** - * The URL of the server. - */ + /** The URL of the server. */ @Value("${baseserver.url}${cxf.path}${cxf.rest.path}/") private URL baseServerUrl; - /** - * The URL of the server for local (non-external) access. - */ + /** The URL of the server for local (non-external) access. */ @Value("${localbaseserver.url}${cxf.path}${cxf.rest.path}/") private URL localBaseServerUrl; - /** - * The REST path of the server. - */ + /** The REST path of the server. */ @Value("${cxf.rest.path}") private String restPath; - /** - * The OIDC redirect URL to return to when authenticated. - */ + /** The OIDC redirect URL to return to when authenticated. */ @Value("${baseserver.url}${callback.path}") private String oidcRedirectUri; - /** - * Determine whether status updates should be done. - */ + /** Determine whether status updates should be done. */ @Value("${status.update}") private boolean updateStatus; - /** - * The type of status service to use. - */ + /** The type of status service to use. */ @Value("${status.update.type}") private StatusServiceType statusType; @@ -186,13 +162,13 @@ interface StringToMachineConverter */ @Bean public MachineManager machineManager() { - if (useSpalloc) { - if (spallocUseJava) { - return new SpallocJavaMachineManagerImpl(); - } + if (!useSpalloc) { + return new FixedMachineManagerImpl(); + } else if (spallocUseJava) { + return new SpallocJavaMachineManagerImpl(); + } else { return new SpallocMachineManagerImpl(); } - return new FixedMachineManagerImpl(); } /** @@ -217,11 +193,11 @@ public NMPIQueueManager queueManager() { public JobExecuterFactory jobExecuterFactory() { if (useXenVms) { return new XenVMExecuterFactory(); - } - if (useDocker) { + } else if (useDocker) { return new DockerExecutorFactory(); + } else { + return new LocalJobExecuterFactory(); } - return new LocalJobExecuterFactory(); } /** @@ -256,14 +232,12 @@ public JobManager jobManager() { @Bean public StatusMonitorManager statusMonitorManager() { if (updateStatus) { - if (statusType == StatusServiceType.STATUS_CAKE) { - return new StatusCakeStatusMonitorManagerImpl(); - } else if (statusType == StatusServiceType.ICINGA2) { - return new Icinga2StatusMonitorManagerImpl(); - } else { - throw new RuntimeException( - "Unknown status service type: " + statusType); - } + return switch (statusType) { + case STATUS_CAKE -> new StatusCakeStatusMonitorManagerImpl(); + case ICINGA2 -> new Icinga2StatusMonitorManagerImpl(); + default -> throw new RuntimeException( + "Unknown status service type: " + statusType); + }; } return new NullStatusMonitorManagerImpl(); } @@ -275,7 +249,7 @@ public StatusMonitorManager statusMonitorManager() { */ @Bean public Server jaxRsServer() { - final var factory = new JAXRSServerFactoryBean(); + var factory = new JAXRSServerFactoryBean(); factory.setAddress(restPath); factory.setBus(ctx.getBean(SpringBus.class)); factory.setServiceBeans(asList(outputManager(), jobManager())); diff --git a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/web/WebApplicationConfig.java b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/web/WebApplicationConfig.java index 06fce22a58..31ad0551d3 100644 --- a/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/web/WebApplicationConfig.java +++ b/SpiNNaker-nmpiserv/src/main/java/uk/ac/manchester/spinnaker/nmpi/web/WebApplicationConfig.java @@ -16,16 +16,16 @@ package uk.ac.manchester.spinnaker.nmpi.web; import static java.lang.System.getProperty; -import static javax.servlet.DispatcherType.ASYNC; -import static javax.servlet.DispatcherType.ERROR; -import static javax.servlet.DispatcherType.REQUEST; +import static jakarta.servlet.DispatcherType.ASYNC; +import static jakarta.servlet.DispatcherType.ERROR; +import static jakarta.servlet.DispatcherType.REQUEST; import java.io.File; import java.io.IOException; import java.util.EnumSet; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; import org.apache.cxf.transport.servlet.CXFServlet; import org.springframework.boot.SpringApplication; @@ -60,11 +60,11 @@ public class WebApplicationConfig implements WebApplicationInitializer { private static final boolean ADD_SERVLET = true; @Override - public void onStartup(final ServletContext container) + public void onStartup(ServletContext container) throws ServletException { System.err.println("Starting..."); try { - final var properties = getPropertySource(); + var properties = getPropertySource(); if (ADD_SERVLET | ADD_FILTER) { container.addListener(getContextLoaderListener(properties)); } @@ -74,7 +74,7 @@ public void onStartup(final ServletContext container) if (ADD_FILTER) { addFilterChain(container); } - } catch (final IOException e) { + } catch (IOException e) { throw new ServletException(e); } } @@ -87,13 +87,11 @@ public void onStartup(final ServletContext container) * @return The listener */ private ContextLoaderListener getContextLoaderListener( - final PropertySource properties) { - final var annotationConfig = - new AnnotationConfigWebApplicationContext(); - annotationConfig.getEnvironment().getPropertySources() - .addFirst(properties); - annotationConfig.register(RemoteSpinnakerBeans.class); - return new ContextLoaderListener(annotationConfig); + PropertySource properties) { + var context = new AnnotationConfigWebApplicationContext(); + context.getEnvironment().getPropertySources().addFirst(properties); + context.register(RemoteSpinnakerBeans.class); + return new ContextLoaderListener(context); } /** @@ -104,8 +102,8 @@ private ContextLoaderListener getContextLoaderListener( * @param properties * The properties of the servlet */ - private void addServlet(final ServletContext container, - final PropertySource properties) { + private void addServlet(ServletContext container, + PropertySource properties) { container.addServlet("cxf", CXFServlet.class) .addMapping(properties.getProperty("cxf.path") + "/*"); } @@ -116,7 +114,7 @@ private void addServlet(final ServletContext container, * @param container * The context of the chain. */ - private void addFilterChain(final ServletContext container) { + private void addFilterChain(ServletContext container) { container.addFilter(FILTER_NAME, new DelegatingFilterProxy(FILTER_NAME)) .addMappingForUrlPatterns(EnumSet.of(REQUEST, ERROR, ASYNC), false, "/*"); @@ -130,7 +128,7 @@ private void addFilterChain(final ServletContext container) { * If something goes wrong */ private PropertySource getPropertySource() throws IOException { - final var source = new File(getProperty(LOCATION_PROPERTY)); + var source = new File(getProperty(LOCATION_PROPERTY)); return new ResourcePropertySource(source.toURI().toString()); } diff --git a/SpiNNaker-nmpiserv/src/test/java/uk/ac/manchester/spinnaker/nmpi/BootTest.java b/SpiNNaker-nmpiserv/src/test/java/uk/ac/manchester/spinnaker/nmpi/BootTest.java index b9750b5ed2..17cf436f3f 100644 --- a/SpiNNaker-nmpiserv/src/test/java/uk/ac/manchester/spinnaker/nmpi/BootTest.java +++ b/SpiNNaker-nmpiserv/src/test/java/uk/ac/manchester/spinnaker/nmpi/BootTest.java @@ -53,7 +53,7 @@ class BootTest extends TestSupport { private NMPIQueueManager nmpiQueueManager; @Test - void testContextBoot() throws InterruptedException { + void testContextBoot() { // If all these bits are there, we declare the application to be working assertNotNull(jobManager); assertNotNull(machineManager); diff --git a/SpiNNaker-py2json/pom.xml b/SpiNNaker-py2json/pom.xml index 296b4f2be7..97ba840905 100644 --- a/SpiNNaker-py2json/pom.xml +++ b/SpiNNaker-py2json/pom.xml @@ -67,8 +67,6 @@ limitations under the License. org.hibernate.validator hibernate-validator - 6.2.5.Final - runtime org.skyscreamer diff --git a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/Configuration.java b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/Configuration.java index 871c4c1284..c5a1326d95 100644 --- a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/Configuration.java +++ b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/Configuration.java @@ -20,13 +20,12 @@ import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - import org.python.core.PyObject; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.utils.validation.IPAddress; import uk.ac.manchester.spinnaker.utils.validation.TCPPort; diff --git a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/Machine.java b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/Machine.java index 8c2f5906d5..69bea7e147 100644 --- a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/Machine.java +++ b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/Machine.java @@ -27,17 +27,16 @@ import java.util.Map; import java.util.Set; -import javax.validation.Valid; -import javax.validation.constraints.AssertTrue; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - import org.python.core.PyObject; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.errorprone.annotations.Keep; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import uk.ac.manchester.spinnaker.machine.board.BMPCoords; import uk.ac.manchester.spinnaker.machine.board.PhysicalCoords; import uk.ac.manchester.spinnaker.machine.board.TriadCoords; diff --git a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/MachineDefinitionConverter.java b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/MachineDefinitionConverter.java index fc0ef137df..8b84e7dee0 100644 --- a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/MachineDefinitionConverter.java +++ b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/MachineDefinitionConverter.java @@ -18,8 +18,8 @@ import static com.fasterxml.jackson.databind.PropertyNamingStrategies.KEBAB_CASE; import static com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS; import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS; +import static jakarta.validation.Validation.byDefaultProvider; import static java.lang.System.exit; -import static javax.validation.Validation.buildDefaultValidatorFactory; import static org.python.core.Py.getSystemState; import static org.python.core.Py.newString; import static org.python.core.PySystemState.initialize; @@ -28,8 +28,7 @@ import java.io.IOException; import java.util.concurrent.Callable; -import javax.validation.ValidatorFactory; - +import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; import org.python.core.PySystemState; import org.python.util.PythonInterpreter; @@ -37,6 +36,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import com.google.errorprone.annotations.MustBeClosed; +import jakarta.validation.ValidatorFactory; import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.ITypeConverter; @@ -51,7 +51,9 @@ */ public class MachineDefinitionConverter implements AutoCloseable { private static final ValidatorFactory VALIDATOR_FACTORY = - buildDefaultValidatorFactory(); + byDefaultProvider().configure() + .messageInterpolator(new ParameterMessageInterpolator()) + .buildValidatorFactory(); private PySystemState sys; diff --git a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/PythonUtils.java b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/PythonUtils.java index 227d17d9ea..f9c7dee3a6 100644 --- a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/PythonUtils.java +++ b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/PythonUtils.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -90,26 +91,6 @@ public static PyObject item(PyObject object, PyObject key) { return object.__getitem__(key); } - /** - * An operation used to convert a Python object into something more - * Java-like. - * - * @param - * The type of value to produce. - * @author Donal Fellows - */ - @FunctionalInterface - public interface Depythonizer { - /** - * Convert an object into a Java value. - * - * @param object - * The value to convert. - * @return The value to produce. - */ - T act(PyObject object); - } - /** * Convert an iterable object into a stream. * @@ -142,10 +123,10 @@ private static Stream stream(PyObject iterable) { * @return The map of collections. */ public static , T, U> Map toCollectingMap( - PyObject mapObject, Depythonizer makeKey, - Supplier makeCollector, Depythonizer makeValue) { - return stream(mapObject).collect(groupingBy(makeKey::act, - mapping(makeValue::act, toCollection(makeCollector)))); + PyObject mapObject, Function makeKey, + Supplier makeCollector, Function makeValue) { + return stream(mapObject).collect(groupingBy(makeKey, + mapping(makeValue, toCollection(makeCollector)))); } /** @@ -164,9 +145,9 @@ public static , T, U> Map toCollectingMap( * @return The map. */ public static Map toMap(PyObject dictObject, - Depythonizer makeKey, Depythonizer makeValue) { - return stream(dictObject).collect(Collectors.toMap(makeKey::act, - key -> makeValue.act(item(dictObject, key)))); + Function makeKey, Function makeValue) { + return stream(dictObject).collect(Collectors.toMap(makeKey, + key -> makeValue.apply(item(dictObject, key)))); } /** @@ -181,9 +162,8 @@ public static Map toMap(PyObject dictObject, * @return The list. */ public static List toList(PyObject listObject, - Depythonizer makeValue) { - return stream(listObject).map(makeValue::act) - .collect(Collectors.toList()); + Function makeValue) { + return stream(listObject).map(makeValue).collect(Collectors.toList()); } /** @@ -198,8 +178,7 @@ public static List toList(PyObject listObject, * @return The set. */ public static Set toSet(PyObject listObject, - Depythonizer makeValue) { - return stream(listObject).map(makeValue::act) - .collect(Collectors.toSet()); + Function makeValue) { + return stream(listObject).map(makeValue).collect(Collectors.toSet()); } } diff --git a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/WithCurrentDirectory.java b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/WithCurrentDirectory.java index c8433cb8e1..ba18a48dc2 100644 --- a/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/WithCurrentDirectory.java +++ b/SpiNNaker-py2json/src/main/java/uk/ac/manchester/spinnaker/py2json/WithCurrentDirectory.java @@ -15,41 +15,44 @@ */ package uk.ac.manchester.spinnaker.py2json; -import static java.lang.String.format; import static java.lang.System.getProperty; +import java.nio.file.Paths; + +import org.python.core.PySystemState; import org.python.util.PythonInterpreter; import com.google.errorprone.annotations.MustBeClosed; /** - * Hack for Java 11 and later, where just changing {@code user.dir} is no longer - * enough. We force the change inside Jython as that's the environment that - * cares. Outside... we shouldn't need to care. + * Sets the Jython notion of what the working directory is for the duration of + * the the context. This isn't properly the current working directory (Java code + * really doesn't care!) but it is what the Python side expects. + *

+ * @see PySystemState#setCurrentWorkingDir(String) */ final class WithCurrentDirectory implements AutoCloseable { - private final PythonInterpreter python; + private final PySystemState sys; + + private final String oldCwd; @MustBeClosed WithCurrentDirectory(PythonInterpreter python, boolean doCd) { - var cwd = getProperty("user.dir"); if (doCd) { - this.python = python; + sys = python.getSystemState(); + oldCwd = sys.getCurrentWorkingDir(); + sys.setCurrentWorkingDir(Paths.get(getProperty("user.dir")) + .toAbsolutePath().toString()); } else { - this.python = null; - } - run(format("import os; __saved=os.getcwd(); os.chdir(r'''%s''')", cwd)); - } - - private void run(String script) { - if (python == null) { - return; + sys = null; + oldCwd = null; } - python.exec(script); } @Override public void close() { - run("os.chdir(__saved)"); + if (sys != null) { + sys.setCurrentWorkingDir(oldCwd); + } } } diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/BufferManagerDatabaseEngine.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/BufferManagerDatabaseEngine.java index 1e4663563a..0911934283 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/BufferManagerDatabaseEngine.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/BufferManagerDatabaseEngine.java @@ -27,7 +27,7 @@ * * @author Donal Fellows */ -public class BufferManagerDatabaseEngine +public final class BufferManagerDatabaseEngine extends DatabaseEngine { private static String sqlDDL = loadResource("buffer_manager.sql"); diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/BufferManagerStorage.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/BufferManagerStorage.java index 213f5a0943..df7d80b46c 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/BufferManagerStorage.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/BufferManagerStorage.java @@ -18,11 +18,10 @@ import java.nio.ByteBuffer; import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.PositiveOrZero; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.machine.HasCoreLocation; import uk.ac.manchester.spinnaker.machine.MemoryLocation; @@ -32,7 +31,7 @@ * * @author Donal Fellows */ -public interface BufferManagerStorage extends ProxyAwareStorage { +public interface BufferManagerStorage extends DatabaseAPI { /** * Retrieves some bytes from the database. The bytes represent the contents * of a DSE region of a particular SpiNNaker core. diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ConnectionProvider.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ConnectionProvider.java deleted file mode 100644 index 7c190df012..0000000000 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ConnectionProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018 The University of Manchester - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package uk.ac.manchester.spinnaker.storage; - -import java.sql.Connection; -import java.sql.SQLException; - -/** - * Indicates a class that can provide database connections to - * suitably-configured databases. - * - * @author Donal Fellows - * @param - * The type of the higher-level access interface that can be used to - * work with the database this class makes connections to. - */ -public interface ConnectionProvider { - /** - * Get a connection to a database, creating it if needed. - * - * @return The configured connection to the database. The database will have - * been seeded with DDL if necessary. - * @throws SQLException - * If anything goes wrong. - */ - Connection getConnection() throws SQLException; - - /** - * @return a storage interface that is suitable for providing support for a - * particular API. - */ - APIType getStorageInterface(); -} diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DSEDatabaseEngine.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DSEDatabaseEngine.java index 64d3b73d13..0ef6943a78 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DSEDatabaseEngine.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DSEDatabaseEngine.java @@ -27,7 +27,7 @@ * * @author Donal Fellows */ -public class DSEDatabaseEngine extends DatabaseEngine { +public final class DSEDatabaseEngine extends DatabaseEngine { private static String sqlDDL = loadResource("dse.sql"); /** diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DSEStorage.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DSEStorage.java index 17e1b250dd..06e4b28b16 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DSEStorage.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DSEStorage.java @@ -19,8 +19,7 @@ import java.util.List; import java.util.Map; -import javax.validation.Valid; - +import jakarta.validation.Valid; import uk.ac.manchester.spinnaker.machine.ChipLocation; import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.machine.MemoryLocation; @@ -31,7 +30,7 @@ * * @author Donal Fellows */ -public interface DSEStorage extends ProxyAwareStorage { +public interface DSEStorage extends DatabaseAPI { /** * Get a list of all ethernets that need to have DSE loading done on them. * diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DatabaseAPI.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DatabaseAPI.java index d6792ff478..1a83973234 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DatabaseAPI.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DatabaseAPI.java @@ -16,9 +16,21 @@ package uk.ac.manchester.spinnaker.storage; /** - * Interface that real database interfaces are subclasses of. + * Interface that real database interfaces are subclasses of. All database + * interfaces defined by this know how to read the proxy information from the + * DB. * * @author Donal Fellows */ public interface DatabaseAPI { + /** + * Get the proxy information from the database. + * + * @return The proxy information, or {@code null} if none defined. When + * there is no proxy, only direct connections to SpiNNaker are + * possible. + * @throws StorageException + * If anything goes wrong. + */ + ProxyInformation getProxyInformation() throws StorageException; } diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DatabaseEngine.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DatabaseEngine.java index 2f36aacbcc..04f5cba7d2 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DatabaseEngine.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/DatabaseEngine.java @@ -42,8 +42,8 @@ * The type of the higher-level access interface that can be used to * work with the database this class makes connections to. */ -public abstract class DatabaseEngine - implements ConnectionProvider { +public abstract sealed class DatabaseEngine + permits BufferManagerDatabaseEngine, DSEDatabaseEngine { private static final Logger log = getLogger(DatabaseEngine.class); /** Busy timeout for SQLite, in milliseconds. */ @@ -92,7 +92,6 @@ protected DatabaseEngine(URI dbUri) { log.info("will manage database at {}", dbUri); } - @Override @MustBeClosed public Connection getConnection() throws SQLException { if (log.isDebugEnabled()) { @@ -110,6 +109,12 @@ public Connection getConnection() throws SQLException { return conn; } + /** + * @return a storage interface that is suitable for providing support for a + * particular API. + */ + public abstract APIType getStorageInterface(); + /** * @return The DDL for initialising this kind of database. */ diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ProxyInformation.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ProxyInformation.java index 92fda24a6d..f0484dd4f8 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ProxyInformation.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/ProxyInformation.java @@ -15,55 +15,22 @@ */ package uk.ac.manchester.spinnaker.storage; +import jakarta.validation.constraints.NotEmpty; import java.util.Map; -import javax.validation.constraints.NotEmpty; - /** * Information about the proxy to allow connection. + * + * @param spallocUrl + * The URL of the spalloc server to connect to. + * @param jobUrl + * The URL of the job to connect to. + * @param headers + * The headers to use for authentication. + * @param cookies + * The cookies to use for authentication. */ -public class ProxyInformation { - /** - * The URL of the spalloc server to connect to. - */ - @NotEmpty - public final String spallocUrl; - - /** - * The URL of the job to connect to. - */ - @NotEmpty - public final String jobUrl; - - /** - * The headers to use for authentication. - */ - @NotEmpty - public final Map headers; - - /** - * The cookies to use for authentication. - */ - @NotEmpty - public final Map cookies; - - /** - * Create a new instance. - * - * @param spallocUrl - * The URL of the Spalloc server. - * @param jobUrl - * The URL of the job. - * @param headers - * The headers to use for authentication. - * @param cookies - * The cookies to use for authentication. - */ - public ProxyInformation(String spallocUrl, String jobUrl, - Map headers, Map cookies) { - this.spallocUrl = spallocUrl; - this.jobUrl = jobUrl; - this.headers = headers; - this.cookies = cookies; - } +public record ProxyInformation(@NotEmpty String spallocUrl, + @NotEmpty String jobUrl, @NotEmpty Map headers, + @NotEmpty Map cookies) { } diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/StorageException.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/StorageException.java index f366635ecb..fa3748e4d1 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/StorageException.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/StorageException.java @@ -15,12 +15,15 @@ */ package uk.ac.manchester.spinnaker.storage; +import java.io.Serial; + /** * Exceptions caused by the storage system. * * @author Donal Fellows */ public class StorageException extends Exception { + @Serial private static final long serialVersionUID = 3553555491656536568L; /** diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQL.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQL.java index 987b2d3720..9f0462623c 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQL.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQL.java @@ -37,58 +37,80 @@ private SQL() { @Parameter("y") @Parameter("processor") @ResultColumn("core_id") - static final String INSERT_LOCATION = - "INSERT INTO core(x, y, processor) VALUES(?, ?, ?) " - + "RETURNING core_id"; + static final String INSERT_LOCATION = """ + INSERT INTO core( + x, y, processor) + VALUES(?, ?, ?) + RETURNING core_id + """; /** Find an existing (x,y,p) record. */ @Parameter("x") @Parameter("y") @Parameter("processor") @ResultColumn("core_id") - static final String GET_LOCATION = "SELECT core_id FROM core" - + " WHERE x = ? AND y = ? AND processor = ? LIMIT 1"; + static final String GET_LOCATION = """ + SELECT core_id + FROM core + WHERE x = ? AND y = ? AND processor = ? + LIMIT 1 + """; /** Create an empty region record. */ @Parameter("core_id") @Parameter("local_region_index") @Parameter("address") @ResultColumn("region_id") - static final String INSERT_REGION = "INSERT INTO " - + "region(core_id, local_region_index, address)" - + " VALUES (?, ?, ?) RETURNING region_id"; + static final String INSERT_REGION = """ + INSERT INTO region( + core_id, local_region_index, address) + VALUES (?, ?, ?) + RETURNING region_id + """; /** Find an existing region record. */ @Parameter("core_id") @Parameter("local_region_index") @ResultColumn("region_id") - static final String GET_REGION = "SELECT region_id FROM region WHERE " - + "core_id = ? AND local_region_index = ? LIMIT 1"; + static final String GET_REGION = """ + SELECT region_id + FROM region + WHERE core_id = ? AND local_region_index = ? + LIMIT 1 + """; /** Append content to a region record. */ @Parameter("content_to_add") @Parameter("content_len") @Parameter("append_time") @Parameter("region_id") - static final String ADD_CONTENT = - "UPDATE region SET content = CAST(? AS BLOB), content_len = ?, " - + "fetches = 1, append_time = ? WHERE region_id = ?"; + static final String ADD_CONTENT = """ + UPDATE region + SET content = CAST(? AS BLOB), content_len = ?, + fetches = 1, append_time = ? + WHERE region_id = ? + """; /** Prepare a region record for handling content in the extra table. */ @Parameter("append_time") @Parameter("region_id") - static final String PREP_EXTRA_CONTENT = - "UPDATE region SET fetches = fetches + 1, append_time = ? " - + "WHERE region_id = ?"; + static final String PREP_EXTRA_CONTENT = """ + UPDATE region + SET fetches = fetches + 1, append_time = ? + WHERE region_id = ? + """; /** Add content to a new row in the extra table. */ @Parameter("region_id") @Parameter("content_to_add") @Parameter("content_len") @ResultColumn("extra_id") - static final String ADD_EXTRA_CONTENT = - "INSERT INTO region_extra(region_id, content, content_len) " - + "VALUES (?, CAST(? AS BLOB), ?) RETURNING extra_id"; + static final String ADD_EXTRA_CONTENT = """ + INSERT INTO region_extra( + region_id, content, content_len) + VALUES (?, CAST(? AS BLOB), ?) + RETURNING extra_id + """; /** * Discover whether region in the main region table is available for storing @@ -96,21 +118,25 @@ private SQL() { */ @Parameter("region_id") @ResultColumn("existing") - static final String GET_MAIN_CONTENT_AVAILABLE = - "SELECT COUNT(*) AS existing FROM region " - + "WHERE region_id = ? AND fetches = 0"; + static final String GET_MAIN_CONTENT_AVAILABLE = """ + SELECT COUNT(*) AS existing + FROM region + WHERE region_id = ? AND fetches = 0 + """; /** * Determine just how much content there is for a row, overall. */ @Parameter("region_id") @ResultColumn("len") - static final String GET_CONTENT_TOTAL_LENGTH = - "SELECT r.content_len + (" - + " SELECT SUM(x.content_len) " - + " FROM region_extra AS x " - + " WHERE x.region_id = r.region_id" - + ") AS len FROM region AS r WHERE region_id = ?"; + static final String GET_CONTENT_TOTAL_LENGTH = """ + SELECT r.content_len + ( + SELECT SUM(x.content_len) + FROM region_extra AS x + WHERE x.region_id = r.region_id) AS len + FROM region AS r + WHERE region_id = ? + """; /** Fetch the current variable state of a region record. */ @Parameter("x") @@ -122,38 +148,46 @@ private SQL() { @ResultColumn("fetches") @ResultColumn("append_time") @ResultColumn("region_id") - static final String FETCH_RECORDING = - "SELECT content, content_len, fetches, append_time, region_id " - + "FROM region_view" - + " WHERE x = ? AND y = ? AND processor = ?" - + " AND local_region_index = ? LIMIT 1"; + static final String FETCH_RECORDING = """ + SELECT content, content_len, fetches, append_time, region_id + FROM region_view + WHERE x = ? AND y = ? AND processor = ? AND local_region_index = ? + LIMIT 1 + """; /** Fetch the current variable state of a region record. */ @Parameter("region_id") @ResultColumn("content") @ResultColumn("content_len") - static final String FETCH_EXTRA_RECORDING = - "SELECT content, content_len FROM region_extra" - + " WHERE region_id = ? ORDER BY extra_id ASC"; + static final String FETCH_EXTRA_RECORDING = """ + SELECT content, content_len + FROM region_extra + WHERE region_id = ? + ORDER BY extra_id ASC + """; /** List the cores with storage. */ @Parameters({}) @ResultColumn("x") @ResultColumn("y") @ResultColumn("processor") - static final String GET_CORES_WITH_STORAGE = - "SELECT DISTINCT x, y, processor FROM region_view" - + " ORDER BY x, y, processor"; + static final String GET_CORES_WITH_STORAGE = """ + SELECT DISTINCT x, y, processor + FROM region_view + ORDER BY x, y, processor + """; /** List the regions of a core with storage. */ @Parameter("x") @Parameter("y") @Parameter("processor") @ResultColumn("local_region_index") - static final String GET_REGIONS_WITH_STORAGE = - "SELECT DISTINCT local_region_index FROM region_view" - + " WHERE x = ? AND y = ? AND processor = ?" - + " ORDER BY local_region_index"; + static final String GET_REGIONS_WITH_STORAGE = """ + SELECT DISTINCT local_region_index + FROM region_view + WHERE x = ? AND y = ? AND processor = ? + ORDER BY local_region_index + """; // ----------------------------------------------------------------- // Data loading ---------------------------------------------------- @@ -167,9 +201,10 @@ private SQL() { @ResultColumn("ethernet_x") @ResultColumn("ethernet_y") @ResultColumn("ip_address") - static final String LIST_ETHERNETS = - "SELECT DISTINCT ethernet_x, ethernet_y, ip_address" - + " FROM core_view"; + static final String LIST_ETHERNETS = """ + SELECT DISTINCT ethernet_x, ethernet_y, ip_address + FROM core_view + """; /** * List the cores of a board (by its Ethernet-enabled chip location) with @@ -181,11 +216,11 @@ private SQL() { @ResultColumn("x") @ResultColumn("y") @ResultColumn("p") - static final String LIST_CORES_TO_LOAD = - "SELECT x, y, p " - + "FROM core_view " - + "WHERE ethernet_x = ? AND ethernet_y = ? " - + "AND is_system = ? "; + static final String LIST_CORES_TO_LOAD = """ + SELECT x, y, p + FROM core_view + WHERE ethernet_x = ? AND ethernet_y = ? AND is_system = ? + """; /** List the regions and sizes of a chip with data to load. */ @Parameter("x") @@ -193,11 +228,12 @@ private SQL() { @Parameter("p") @ResultColumn("region_num") @ResultColumn("size") - static final String GET_REGION_SIZES = - "SELECT region_num, size " - + "FROM region " - + "WHERE x = ? AND y = ? AND p = ? " - + "ORDER BY region_num"; + static final String GET_REGION_SIZES = """ + SELECT region_num, size + FROM region + WHERE x = ? AND y = ? AND p = ? + ORDER BY region_num + """; /** Get the data to load for a particular core. */ @Parameter("x") @@ -206,11 +242,12 @@ private SQL() { @ResultColumn("region_num") @ResultColumn("content") @ResultColumn("pointer") - static final String GET_REGION_POINTER_AND_CONTEXT = - "SELECT region_num, content, pointer " - + "FROM pointer_content_view " - + "WHERE x = ? AND y = ? AND p = ? " - + "ORDER BY region_num"; + static final String GET_REGION_POINTER_AND_CONTEXT = """ + SELECT region_num, content, pointer + FROM pointer_content_view + WHERE x = ? AND y = ? AND p = ? + ORDER BY region_num + """; /** * Store the start_address for the core. @@ -219,19 +256,22 @@ private SQL() { @Parameter("x") @Parameter("y") @Parameter("p") - static final String SET_START_ADDRESS = "UPDATE core " - + "SET start_address = ? " - + "WHERE x = ? AND y = ? AND p = ?"; + static final String SET_START_ADDRESS = """ + UPDATE core + SET start_address = ? + WHERE x = ? AND y = ? AND p = ? + """; /** Get the start address for this core. */ @Parameter("x") @Parameter("y") @Parameter("p") @ResultColumn("start_address") - static final String GET_START_ADDRESS = - "SELECT start_address " - + "FROM core " - + "WHERE x = ? AND y = ? AND p = ? "; + static final String GET_START_ADDRESS = """ + SELECT start_address + FROM core + WHERE x = ? AND y = ? AND p = ? + """; /** * Store the pointer for this reason. @@ -241,28 +281,37 @@ private SQL() { @Parameter("y") @Parameter("p") @Parameter("region_num") - static final String SET_REGION_POINTER = "UPDATE region " - + "SET pointer = ?" - + "WHERE x = ? AND y = ? and p = ? and region_num = ?"; + static final String SET_REGION_POINTER = """ + UPDATE region + SET pointer = ? + WHERE x = ? AND y = ? and p = ? and region_num = ? + """; /** Get the app_id. */ @ResultColumn("app_id") - static final String GET_APP_ID = - "SELECT app_id " - + "FROM app_id "; + static final String GET_APP_ID = """ + SELECT app_id + FROM app_id + """; /** * The name of the result containing the spalloc URI. + *

+ * Must match {@link #GET_PROXY_INFORMATION}. */ static final String SPALLOC_URI = "service uri"; /** * The name of the result containing the proxy URI. + *

+ * Must match {@link #GET_PROXY_INFORMATION}. */ static final String PROXY_URI = "job uri"; /** - * The kind of the result containing a proxy cookie. + * The name of the result containing the proxy Authorization cookie. + *

+ * Must match {@link #GET_PROXY_INFORMATION}. */ static final String COOKIE = "COOKIE"; @@ -279,9 +328,15 @@ private SQL() { /** * Get information about the proxy. */ - static final String GET_PROXY_INFORMATION = - "SELECT kind, name, value FROM proxy_configuration WHERE " - + "((kind = '" + SPALLOC + "' AND name = '" + PROXY_URI + "') OR" - + " (kind = '" + HEADER + "') OR (kind = '" + COOKIE + "') OR" - + " (kind = '" + SPALLOC + "' AND name = '" + SPALLOC_URI + "'))"; + @ResultColumn("kind") + @ResultColumn("name") + @ResultColumn("value") + static final String GET_PROXY_INFORMATION = """ + SELECT kind, name, value + FROM proxy_configuration + WHERE ((kind = 'SPALLOC' AND name = 'job uri') + OR (kind = 'HEADER') + OR (kind = 'COOKIE') + OR (kind = 'SPALLOC' AND name = 'service uri')) + """; } diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteBufferStorage.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteBufferStorage.java index b77069d2e2..58e01aac8e 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteBufferStorage.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteBufferStorage.java @@ -53,20 +53,19 @@ * * @author Donal Fellows */ -public class SQLiteBufferStorage - extends SQLiteProxyStorage - implements BufferManagerStorage { +public final class SQLiteBufferStorage extends + SQLiteStorage implements BufferManagerStorage { private static final Logger log = getLogger(SQLiteBufferStorage.class); /** * Create an instance. * - * @param connectionProvider - * The connection provider that will be asked for how to talk SQL - * to the database. + * @param db + * The database engine that will be asked for how to talk SQL to + * the database. */ - public SQLiteBufferStorage(BufferManagerDatabaseEngine connectionProvider) { - super(connectionProvider); + public SQLiteBufferStorage(BufferManagerDatabaseEngine db) { + super(db); } private static int getRecordingCore(Connection conn, CoreLocation core) @@ -107,7 +106,7 @@ private static int getRecordingRegion(Connection conn, int coreID, try (var s = conn.prepareStatement(INSERT_REGION)) { // core_id, local_region_index, address setArguments(s, coreID, region.regionIndex, - region.startAddress.address); + region.startAddress.address()); try (var rs = s.executeQuery()) { while (rs.next()) { return rs.getInt("region_id"); diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteDataSpecStorage.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteDataSpecStorage.java index bb7a4b2c6f..ce9281608f 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteDataSpecStorage.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteDataSpecStorage.java @@ -15,8 +15,19 @@ */ package uk.ac.manchester.spinnaker.storage.sqlite; -import java.nio.ByteBuffer; import static java.nio.ByteBuffer.wrap; +import static java.util.Objects.nonNull; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_APP_ID; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_REGION_POINTER_AND_CONTEXT; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_REGION_SIZES; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_START_ADDRESS; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.LIST_CORES_TO_LOAD; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.LIST_ETHERNETS; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SET_REGION_POINTER; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SET_START_ADDRESS; +import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.readOnly; + +import java.nio.ByteBuffer; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; @@ -26,16 +37,6 @@ import uk.ac.manchester.spinnaker.machine.CoreLocation; import uk.ac.manchester.spinnaker.machine.MemoryLocation; - -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_APP_ID; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_REGION_POINTER_AND_CONTEXT; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_REGION_SIZES; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_START_ADDRESS; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.LIST_ETHERNETS; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SET_REGION_POINTER; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SET_START_ADDRESS; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.LIST_CORES_TO_LOAD; - import uk.ac.manchester.spinnaker.storage.DSEDatabaseEngine; import uk.ac.manchester.spinnaker.storage.DSEStorage; import uk.ac.manchester.spinnaker.storage.RegionInfo; @@ -46,17 +47,17 @@ * * @author Donal Fellows */ -public class SQLiteDataSpecStorage extends SQLiteProxyStorage +public final class SQLiteDataSpecStorage extends SQLiteStorage implements DSEStorage { /** * Create an instance. * - * @param connectionProvider - * The connection provider that will be asked for how to talk SQL - * to the database. + * @param db + * The database engine that will be asked for how to talk SQL to + * the database. */ - public SQLiteDataSpecStorage(DSEDatabaseEngine connectionProvider) { - super(connectionProvider); + public SQLiteDataSpecStorage(DSEDatabaseEngine db) { + super(db); } @Override @@ -79,11 +80,11 @@ private static List listEthernetsToLoad(Connection conn) } private static EthernetImpl sanitise(Ethernet ethernet) { - if (!(ethernet instanceof EthernetImpl)) { - throw new IllegalArgumentException("can only list cores" - + " for ethernets described by this class"); + if (ethernet instanceof EthernetImpl eth) { + return eth; } - return (EthernetImpl) ethernet; + throw new IllegalArgumentException("can only list cores" + + " for ethernets described by this class"); } @Override @@ -143,39 +144,42 @@ public HashMap getRegionPointersAndContent( private static HashMap getRegionPointersAndContent( Connection conn, CoreLocation xyp) throws SQLException { - HashMap results = - new HashMap(); try (var s = conn.prepareStatement(GET_REGION_POINTER_AND_CONTEXT)) { // x, y, p setArguments(s, xyp.getX(), xyp.getY(), xyp.getP()); try (var rs = s.executeQuery()) { + var results = new HashMap(); while (rs.next()) { - ByteBuffer content = null; - if (rs.getBytes("content") != null) { - content = - wrap(rs.getBytes("content")).asReadOnlyBuffer(); - } - var info = new RegionInfo( - content, new MemoryLocation(rs.getInt("pointer"))); - results.put(rs.getInt("region_num"), info); + results.put(rs.getInt("region_num"), + new RegionInfo( + wrapIfNotNull(rs.getBytes("content")), + new MemoryLocation(rs.getInt("pointer")))); } + return results; } - return results; } } + private static ByteBuffer wrapIfNotNull(byte[] buffer) { + if (nonNull(buffer)) { + return readOnly(wrap(buffer)); + } + return null; + } + @Override public void setStartAddress(CoreLocation xyp, MemoryLocation start) throws StorageException { - callV(conn -> setStartAddres(conn, xyp, start), + callV(conn -> setStartAddress(conn, xyp, start), "saving data loading metadata"); } - private static void setStartAddres(Connection conn, + private static void setStartAddress(Connection conn, CoreLocation xyp, MemoryLocation start) throws SQLException { try (var s = conn.prepareStatement(SET_START_ADDRESS)) { // start_address, x, y, p - setArguments(s, start.address, xyp.getX(), xyp.getY(), xyp.getP()); + setArguments(s, start.address(), xyp.getX(), xyp.getY(), + xyp.getP()); s.executeUpdate(); } } @@ -246,11 +250,8 @@ private EthernetImpl(int etherx, int ethery, String addr) { @Override public boolean equals(Object other) { - if (!(other instanceof EthernetImpl)) { - return false; - } - var b = (EthernetImpl) other; - return location == b.location; + return (other instanceof EthernetImpl b) + && (location == b.location); } @Override diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteProxyStorage.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteProxyStorage.java deleted file mode 100644 index ac16344354..0000000000 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteProxyStorage.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2022 The University of Manchester - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package uk.ac.manchester.spinnaker.storage.sqlite; - -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_PROXY_INFORMATION; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.COOKIE; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.HEADER; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SPALLOC; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.PROXY_URI; -import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SPALLOC_URI; - -import java.sql.Connection; -import java.sql.SQLException; -import java.util.HashMap; - -import uk.ac.manchester.spinnaker.storage.ConnectionProvider; -import uk.ac.manchester.spinnaker.storage.DatabaseAPI; -import uk.ac.manchester.spinnaker.storage.ProxyAwareStorage; -import uk.ac.manchester.spinnaker.storage.ProxyInformation; -import uk.ac.manchester.spinnaker.storage.StorageException; - -abstract class SQLiteProxyStorage - extends SQLiteConnectionManager implements ProxyAwareStorage { - - protected SQLiteProxyStorage(ConnectionProvider connProvider) { - super(connProvider); - } - - @Override - public ProxyInformation getProxyInformation() throws StorageException { - return callR(conn -> getProxyInfo(conn), "get proxy"); - } - - /** - * Get the proxy information from a database. - * - * @param conn - * The connection to read the data from. - * @return The proxy information. - * @throws SQLException - * If there is an error reading the database. - * @throws IllegalStateException - * If a bad row is retrieved; should be unreachable if SQL is - * synched to code. - */ - private ProxyInformation getProxyInfo(Connection conn) throws SQLException { - String spallocUri = null; - String jobUri = null; - var headers = new HashMap(); - var cookies = new HashMap(); - - try (var s = conn.prepareStatement(GET_PROXY_INFORMATION); - var rs = s.executeQuery()) { - while (rs.next()) { - var kind = rs.getString("kind"); - var name = rs.getString("name"); - var value = rs.getString("value"); - if (name == null || value == null) { - continue; - } - switch (kind) { - case SPALLOC: - switch (name) { - case SPALLOC_URI: - spallocUri = value; - break; - case PROXY_URI: - jobUri = value; - break; - default: - throw new IllegalStateException("unreachable reached"); - } - break; - - case COOKIE: - cookies.put(name, value); - break; - case HEADER: - headers.put(name, value); - break; - default: - throw new IllegalStateException("unreachable reached"); - } - } - } - // If we don't have all pieces of info, we can't talk to the proxy - if (spallocUri == null || jobUri == null) { - return null; - } - return new ProxyInformation(spallocUri, jobUri, headers, cookies); - } -} diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteConnectionManager.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteStorage.java similarity index 61% rename from SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteConnectionManager.java rename to SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteStorage.java index d0543f9cc2..dccad7e424 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteConnectionManager.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/sqlite/SQLiteStorage.java @@ -17,18 +17,26 @@ import static org.slf4j.LoggerFactory.getLogger; import static org.sqlite.SQLiteErrorCode.SQLITE_BUSY; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.COOKIE; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.GET_PROXY_INFORMATION; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.HEADER; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.PROXY_URI; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SPALLOC; +import static uk.ac.manchester.spinnaker.storage.sqlite.SQL.SPALLOC_URI; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.HashMap; import org.slf4j.Logger; import org.sqlite.SQLiteException; import com.google.errorprone.annotations.concurrent.GuardedBy; -import uk.ac.manchester.spinnaker.storage.ConnectionProvider; import uk.ac.manchester.spinnaker.storage.DatabaseAPI; +import uk.ac.manchester.spinnaker.storage.DatabaseEngine; +import uk.ac.manchester.spinnaker.storage.ProxyInformation; import uk.ac.manchester.spinnaker.storage.StorageException; /** @@ -39,20 +47,21 @@ * The type of the connections used inside this class. Probably the * type of the concrete subclass of this class. */ -abstract class SQLiteConnectionManager { - private static final Logger log = getLogger(SQLiteConnectionManager.class); +abstract sealed class SQLiteStorage + implements DatabaseAPI + permits SQLiteBufferStorage, SQLiteDataSpecStorage { + private static final Logger log = getLogger(SQLiteStorage.class); @GuardedBy("itself") - private final ConnectionProvider connProvider; + private final DatabaseEngine db; /** - * @param connProvider + * @param db * The source of database connections. * @see Connection */ - protected SQLiteConnectionManager( - ConnectionProvider connProvider) { - this.connProvider = connProvider; + protected SQLiteStorage(DatabaseEngine db) { + this.db = db; } /** @@ -71,7 +80,7 @@ protected static void setArguments(PreparedStatement statement, Object... args) throws SQLException { statement.clearParameters(); int idx = 1; - for (Object o : args) { + for (var o : args) { statement.setObject(idx, o); idx++; } @@ -131,8 +140,8 @@ interface CallWithoutResult { */ final T callR(CallWithResult call, String actionDescription) throws StorageException { - synchronized (connProvider) { - try (var conn = connProvider.getConnection()) { + synchronized (db) { + try (var conn = db.getConnection()) { startTransaction(conn); try { var result = call.call(conn); @@ -160,8 +169,8 @@ final T callR(CallWithResult call, String actionDescription) */ final void callV(CallWithoutResult call, String actionDescription) throws StorageException { - synchronized (connProvider) { - try (var conn = connProvider.getConnection()) { + synchronized (db) { + try (var conn = db.getConnection()) { startTransaction(conn); try { call.call(conn); @@ -187,20 +196,79 @@ private void startTransaction(Connection conn) throws SQLException { return; } catch (SQLiteException e) { switch (e.getResultCode()) { - case SQLITE_BUSY: - case SQLITE_BUSY_RECOVERY: - case SQLITE_BUSY_SNAPSHOT: - case SQLITE_BUSY_TIMEOUT: - if (log.isDebugEnabled()) { - log.debug("database busy; trying to relock"); - } + case SQLITE_BUSY, SQLITE_BUSY_RECOVERY, SQLITE_BUSY_SNAPSHOT, + SQLITE_BUSY_TIMEOUT -> { + log.debug("database busy; trying to relock"); code = e.getResultCode(); - continue; - default: - throw e; + } + default -> throw e; } } } throw new SQLiteException("database very busy", code); } + + @Override + public ProxyInformation getProxyInformation() throws StorageException { + return callR(conn -> getProxyInfo(conn), "get proxy"); + } + + /** + * Get the proxy information from a database. + * + * @param conn + * The connection to read the data from. + * @return The proxy information. + * @throws SQLException + * If there is an error reading the database. + * @throws Unreachable + * If a bad row is retrieved; should be unreachable if SQL is + * synched to code. + */ + @SuppressWarnings("checkstyle:InnerAssignment") // Rule is misapplying + private ProxyInformation getProxyInfo(Connection conn) throws SQLException { + String spallocUri = null; + String jobUri = null; + var headers = new HashMap(); + var cookies = new HashMap(); + + try (var s = conn.prepareStatement(GET_PROXY_INFORMATION); + var rs = s.executeQuery()) { + while (rs.next()) { + var kind = rs.getString("kind"); + var name = rs.getString("name"); + var value = rs.getString("value"); + if (kind == null || name == null || value == null) { + continue; + } + switch (kind) { + case SPALLOC -> { + switch (name) { + case SPALLOC_URI -> spallocUri = value; + case PROXY_URI -> jobUri = value; + default -> throw new Unreachable(); + } + } + + case COOKIE -> cookies.put(name, value); + case HEADER -> headers.put(name, value); + default -> throw new Unreachable(); + } + } + } + // If we don't have all pieces of info, we can't talk to the proxy + if (spallocUri == null || jobUri == null) { + return null; + } + return new ProxyInformation(spallocUri, jobUri, headers, cookies); + } + + /** Thrown when an unreachable state is reached. */ + private static class Unreachable extends IllegalStateException { + private static final long serialVersionUID = 1L; + + Unreachable() { + super("unreachable reached"); + } + } } diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTPrepared.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTPrepared.java index 3aec98532a..d5c74a6ef5 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTPrepared.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTPrepared.java @@ -41,7 +41,8 @@ * * @author Donal Fellows */ -class OTPrepared extends OTStatement implements PreparedStatement { +sealed class OTPrepared extends OTStatement + implements PreparedStatement permits OTCallable { private final PreparedStatement s; OTPrepared(OneThread ot, PreparedStatement s) { diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTResults.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTResults.java index cf00c02e24..895a80f0d1 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTResults.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTResults.java @@ -764,10 +764,10 @@ public Statement getStatement() throws SQLException { } validateThread(); var st = r.getStatement(); - if (st instanceof CallableStatement) { - return wrap((CallableStatement) st); - } else if (st instanceof PreparedStatement) { - return wrap((PreparedStatement) st); + if (st instanceof CallableStatement cs) { + return wrap(cs); + } else if (st instanceof PreparedStatement ps) { + return wrap(ps); } else { return wrap(st); } diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTStatement.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTStatement.java index 3ccffd68d6..e93f0922fc 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTStatement.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTStatement.java @@ -26,7 +26,8 @@ * * @author Donal Fellows */ -class OTStatement extends OTWrapper implements Statement { +sealed class OTStatement extends OTWrapper + implements Statement permits OTPrepared { private final Statement s; OTStatement(OneThread ot, Statement s) { diff --git a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTWrapper.java b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTWrapper.java index 5c65ac300b..5bb7f2e903 100644 --- a/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTWrapper.java +++ b/SpiNNaker-storage/src/main/java/uk/ac/manchester/spinnaker/storage/threading/OTWrapper.java @@ -32,7 +32,8 @@ * * @author Donal Fellows */ -abstract class OTWrapper implements Wrapper { +abstract sealed class OTWrapper implements + Wrapper permits OTConnection, OTMeta, OTResults, OTRSMeta, OTStatement { private final OneThread ot; private final Wrapper w; diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/ByteBufferUtils.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/ByteBufferUtils.java index 025def7b7b..f5e908e462 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/ByteBufferUtils.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/ByteBufferUtils.java @@ -28,21 +28,32 @@ public abstract class ByteBufferUtils { private ByteBufferUtils() { } + private static final int WORD_SIZE = 4; + /** - * Make a slice of a byte buffer without modifying the original buffer. + * Allocate a new little-endian byte buffer. * - * @param src - * The originating buffer. - * @param from - * The offset into the originating buffer where the slice starts. - * @param len - * The length of the slice. - * @return The little-endian slice. This will be read-only if and only if - * the original buffer is read-only. + * @param capacity + * The capacity of the buffer. + * @return The buffer. + */ + public static ByteBuffer alloc(int capacity) { + return ByteBuffer.allocate(capacity).order(LITTLE_ENDIAN); + } + + /** + * Convert a word to a buffer that could form part of a message understood + * by SpiNNaker. + * + * @param value + * The value to put in the buffer as a single 32-bit word. + * @return The buffer, flipped. The buffer is writable and has a backing + * array. */ - public static ByteBuffer slice(ByteBuffer src, int from, int len) { - var s = src.duplicate().position(from).slice(); - return s.limit(len).order(LITTLE_ENDIAN); + public static ByteBuffer wordAsBuffer(int value) { + var b = alloc(WORD_SIZE); + b.putInt(value).flip(); + return b; } /** @@ -75,9 +86,8 @@ public static ByteBuffer limitSlice(ByteBuffer src, int maxSize) { */ public static MappableIterable sliceUp(ByteBuffer src, int chunkSize) { + var b = src.duplicate(); return () -> new Iterator<>() { - final ByteBuffer b = src.duplicate(); - @Override public boolean hasNext() { return b.hasRemaining(); @@ -123,4 +133,15 @@ public static ByteBuffer read(InputStream data, ByteBuffer workingBuffer, } return tmp.limit(size); } + + /** + * Convert the remaining bytes in a buffer into a read-only buffer. + * + * @param buffer + * message buffer to convert + * @return The read-only view. + */ + public static ByteBuffer readOnly(ByteBuffer buffer) { + return buffer.asReadOnlyBuffer().order(LITTLE_ENDIAN); + } } diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/CollectionUtils.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/CollectionUtils.java index d9ea8e3e1a..3438cae2c1 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/CollectionUtils.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/CollectionUtils.java @@ -24,9 +24,9 @@ import static java.util.EnumSet.noneOf; import static java.util.Objects.isNull; import static java.util.stream.Collectors.toCollection; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toSet; +import static java.util.stream.Collectors.toUnmodifiableList; import static java.util.stream.Collectors.toUnmodifiableMap; +import static uk.ac.manchester.spinnaker.utils.MathUtils.ceildiv; import java.util.ArrayList; import java.util.Collection; @@ -147,12 +147,11 @@ public static Function curry(BiFunction fn, */ public static Collection> batch(int batchSize, List input) { - return unmodifiableCollection(IntStream.range(0, - (input.size() + batchSize - 1) / batchSize) + return IntStream.range(0, ceildiv(input.size(), batchSize)) .map(i -> i * batchSize) .mapToObj(idx -> unmodifiableCollection( input.subList(idx, min(input.size(), idx + batchSize)))) - .collect(toList())); + .collect(toUnmodifiableList()); } /** @@ -166,27 +165,11 @@ public static Collection> batch(int batchSize, * The input list. * @param fun * How to map an element. - * @return The output list. + * @return The output list. Unmodifiable. */ @UsedInJavadocOnly(Stream.class) public static List lmap(Collection list, Function fun) { - return list.stream().map(fun).collect(toList()); - } - - /** - * Parse a comma-separated string into an unordered set of items. - * - * @param - * The type of elements of the set. - * @param str - * The string to parse. - * @param mapper - * How to get an element from a piece of string. - * @return The set of items. The set is unordered. - */ - public static Set parseCommaSeparatedSet(String str, - Function mapper) { - return stream(str.split(",")).map(mapper).collect(toSet()); + return list.stream().map(fun).collect(toUnmodifiableList()); } /** diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/DefaultMap.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/DefaultMap.java index c1bcf90901..d79f7a8c6c 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/DefaultMap.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/DefaultMap.java @@ -17,6 +17,7 @@ import static java.util.Objects.requireNonNull; +import java.io.Serial; import java.util.HashMap; import java.util.function.BiFunction; import java.util.function.Function; @@ -36,6 +37,7 @@ * The type of values. */ public class DefaultMap extends HashMap { + @Serial private static final long serialVersionUID = -3805864660424802906L; /** diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/InetFactory.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/InetFactory.java index bf899b00c9..4776992d9d 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/InetFactory.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/InetFactory.java @@ -15,6 +15,7 @@ */ package uk.ac.manchester.spinnaker.utils; +import java.io.Serial; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -126,6 +127,7 @@ public static Inet4Address getByNameQuietly(String host) { */ public static class Inet6NotSupportedException extends UnknownHostException { + @Serial private static final long serialVersionUID = -7430619278827122304L; /** diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/OptionalUtils.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/OptionalUtils.java index a2e57d6a7f..0ac828c653 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/OptionalUtils.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/OptionalUtils.java @@ -71,6 +71,7 @@ public static Optional apply(Optional source, */ public static U ifElse(Optional source, Function ifPresent, Supplier ifAbsent) { + // Would use ifPresentOrElse, except that's void result return source.map(ifPresent).orElseGet(ifAbsent); } diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/Ping.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/Ping.java index b95fa765ee..5c1567434f 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/Ping.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/Ping.java @@ -34,6 +34,18 @@ public abstract class Ping { private static final int PING_COUNT = 10; + /** + * Are we running in Github Actions? That network environment is hostile to + * this class. + * + * @see Github + * Actions Documentation → Variables → Default environment + * variables + */ + private static final boolean IN_GITHUB_ACTION = + Boolean.getBoolean("GITHUB_ACTIONS"); + private Ping() { } @@ -89,6 +101,10 @@ private static void drain(InputStream is) { */ @CheckReturnValue public static int ping(String address) { + if (IN_GITHUB_ACTION) { + // Github runs in Azure, which blocks ping; pretend success + return 0; + } int i = 0; while (true) { int result = ping1(address); diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/ValueHolder.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/ValueHolder.java index 58ee28b07a..80efe9dc24 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/ValueHolder.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/ValueHolder.java @@ -44,6 +44,15 @@ public ValueHolder(T value) { this.value = value; } + /** + * Get whether a (non-{@code null}) value is absent. + * + * @return Whether a value is absent and this holder is empty. + */ + public boolean isEmpty() { + return value == null; + } + /** * Get the value held. * diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/IPAddress.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/IPAddress.java index 8ef279d0f5..88a0830284 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/IPAddress.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/IPAddress.java @@ -29,10 +29,10 @@ import java.net.UnknownHostException; import java.util.regex.Pattern; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; /** * Validates that a string looks like an IP address. A string is considered to diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/TCPPort.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/TCPPort.java index c25990f4ce..916d12c7f6 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/TCPPort.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/TCPPort.java @@ -26,10 +26,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; /** * Validates that a number looks like a TCP port. Always accepts {@code null}. diff --git a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/UDPPort.java b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/UDPPort.java index 71e0883b23..6a5d61a077 100644 --- a/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/UDPPort.java +++ b/SpiNNaker-utils/src/main/java/uk/ac/manchester/spinnaker/utils/validation/UDPPort.java @@ -26,10 +26,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.validation.Constraint; -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; -import javax.validation.Payload; +import jakarta.validation.Constraint; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.Payload; /** * Validates that a number looks like a UDP port. Always accepts {@code null}. diff --git a/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestByteBufferUtils.java b/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestByteBufferUtils.java index fb0c369c1d..994100bb58 100644 --- a/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestByteBufferUtils.java +++ b/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestByteBufferUtils.java @@ -15,12 +15,11 @@ */ package uk.ac.manchester.spinnaker.utils; +import static java.nio.ByteOrder.LITTLE_ENDIAN; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.nio.ReadOnlyBufferException; import org.junit.jupiter.api.Test; @@ -32,8 +31,8 @@ public class TestByteBufferUtils { @Test public void testSlice() { - var bb = ByteBuffer.allocate(12).order(ByteOrder.LITTLE_ENDIAN); - var s = ByteBufferUtils.slice(bb, 4, 4); + var bb = ByteBufferUtils.alloc(12); + var s = bb.slice(4, 4).order(LITTLE_ENDIAN); s.putInt(0x01020304); // Check range enforcement @@ -45,13 +44,13 @@ public void testSlice() { assertEquals(0, bb.getInt()); // Check read-only carried around - bb = bb.asReadOnlyBuffer(); - var r = ByteBufferUtils.slice(bb, 4, 4); + bb = ByteBufferUtils.readOnly(bb); + var r = bb.slice(4, 4).order(LITTLE_ENDIAN); assertThrows(ReadOnlyBufferException.class, () -> r.putInt(0x5060708)); assertEquals(0x01020304, r.getInt()); // Check range sanity enforced - assertThrows(IllegalArgumentException.class, - () -> ByteBufferUtils.slice(r, 0, 16)); + assertThrows(IndexOutOfBoundsException.class, + () -> r.slice(0, 16)); } } diff --git a/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestDefaultMap.java b/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestDefaultMap.java index 16bfbe116a..ffb52fdc40 100644 --- a/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestDefaultMap.java +++ b/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestDefaultMap.java @@ -45,8 +45,7 @@ public void testUntyped() { @Test public void testTyped() { - // Explicit to work around weird issue in Java 14 compiler - Map> instance = new DefaultMap<>(ArrayList::new); + var instance = new DefaultMap>(ArrayList::new); var foo = instance.get("foo"); assertTrue(foo instanceof ArrayList); // foo.add("a"); @@ -78,8 +77,7 @@ public void testBad() { @Test public void testKeyAware() { - DefaultMap instance = - DefaultMap.newAdvancedDefaultMap(new Doubler()); + var instance = DefaultMap.newAdvancedDefaultMap(new Doubler()); var two = instance.get(1); assertEquals(2, two.intValue()); } diff --git a/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestMappableIterable.java b/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestMappableIterable.java index f5a1ad8705..4dd5126501 100644 --- a/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestMappableIterable.java +++ b/SpiNNaker-utils/src/test/java/uk/ac/manchester/spinnaker/utils/TestMappableIterable.java @@ -40,7 +40,7 @@ public void testMap() { MappableIterable mi = values::iterator; int i = 3; - for (Integer value : mi.map(x -> x * 3)) { + for (var value : mi.map(x -> x * 3)) { assertEquals(i, value); i += 3; } @@ -51,7 +51,7 @@ public void testMap() { mi = () -> empty.iterator(); i = 0; - for (Integer value : mi.map(x -> 1 << x)) { + for (var value : mi.map(x -> 1 << x)) { i += value; } assertEquals(i, 0); @@ -70,25 +70,25 @@ public void testFilter() { MappableIterable mi = values::iterator; int i = 1; - for (Integer value : mi.filter(x -> (x & 1) > 0)) { + for (var value : mi.filter(x -> (x & 1) > 0)) { assertEquals(i, value); i += 2; } assertEquals(i, 7); i = 2; - for (Integer value : mi.filter(x -> (x & 1) == 0)) { + for (var value : mi.filter(x -> (x & 1) == 0)) { assertEquals(i, value); i += 2; } assertEquals(i, 6); - for (Integer value : mi.filter(x -> x < 1)) { + for (var value : mi.filter(x -> x < 1)) { assertUnreachable(); } mi = () -> empty.iterator(); - for (Integer value : mi.filter(x -> { + for (var value : mi.filter(x -> { assertUnreachable(); return true; })) { diff --git a/keycloak-management-client/pom.xml b/keycloak-management-client/pom.xml index 2f516f0631..701aadb7a5 100644 --- a/keycloak-management-client/pom.xml +++ b/keycloak-management-client/pom.xml @@ -31,10 +31,6 @@ limitations under the License. SpiNNaker-allocserv. You probably don't need to ever use this code! - - 11 - - com.fasterxml.jackson.core diff --git a/keycloak-management-client/src/main/java/uk/ac/manchester/spinnaker/tools/CredentialDB.java b/keycloak-management-client/src/main/java/uk/ac/manchester/spinnaker/tools/CredentialDB.java index 165e52a977..830e845ddc 100644 --- a/keycloak-management-client/src/main/java/uk/ac/manchester/spinnaker/tools/CredentialDB.java +++ b/keycloak-management-client/src/main/java/uk/ac/manchester/spinnaker/tools/CredentialDB.java @@ -52,10 +52,16 @@ public CredentialDB(File databaseFile) throws SQLException, IOException { db = (SQLiteConnection) config.createConnection( "jdbc:sqlite:" + databaseFile.getCanonicalPath()); try (var s = db.createStatement()) { - s.execute("CREATE TABLE IF NOT EXISTS tokens(" - + "client TEXT UNIQUE NOT NULL, token TEXT NOT NULL)"); - s.execute("CREATE TABLE IF NOT EXISTS users(" - + "name TEXT UNIQUE NOT NULL, pass TEXT NOT NULL)"); + s.execute(""" + CREATE TABLE IF NOT EXISTS tokens( + client TEXT UNIQUE NOT NULL, + token TEXT NOT NULL) + """); + s.execute(""" + CREATE TABLE IF NOT EXISTS users( + name TEXT UNIQUE NOT NULL, + pass TEXT NOT NULL) + """); } } @@ -79,8 +85,11 @@ final class DBContainedCredentials implements EBRAINSDevCredentials { /** Make an instance. */ DBContainedCredentials() throws SQLException, IllegalStateException { try (var s = db.createStatement(); - var rs = s.executeQuery( - "SELECT name, pass FROM users LIMIT 1")) { + var rs = s.executeQuery(""" + SELECT name, pass + FROM users + LIMIT 1 + """)) { if (rs.next()) { name = rs.getString("name"); pass = rs.getString("pass"); @@ -112,8 +121,11 @@ public String getPass() { */ final void saveCredentials(EBRAINSDevCredentials creds) throws SQLException { - try (var s = db.prepareStatement( - "INSERT OR REPLACE INTO users(name, pass) VALUES (?, ?)")) { + try (var s = db.prepareStatement(""" + INSERT OR REPLACE INTO users( + name, pass) + VALUES (?, ?) + """)) { s.setString(1, creds.getUser()); s.setString(2, creds.getPass()); s.executeUpdate(); @@ -128,8 +140,12 @@ final void saveCredentials(EBRAINSDevCredentials creds) * @return The token, or {@code null} if no token is available. */ final String getToken(String clientId) { - try (var s = db.prepareStatement( - "SELECT token FROM tokens WHERE client = ? LIMIT 1")) { + try (var s = db.prepareStatement(""" + SELECT token + FROM tokens + WHERE client = ? + LIMIT 1 + """)) { s.setString(1, requireNonNull(clientId)); try (var rs = s.executeQuery()) { while (rs.next()) { @@ -157,9 +173,11 @@ final String getToken(String clientId) { * If database access fails. */ final void saveToken(String clientId, String token) throws SQLException { - try (var s = db.prepareStatement( - "INSERT OR REPLACE INTO tokens(client, token) " - + "VALUES (?, ?)")) { + try (var s = db.prepareStatement(""" + INSERT OR REPLACE INTO tokens( + client, token) + VALUES (?, ?) + """)) { s.setString(1, clientId); s.setString(2, token); s.executeUpdate(); diff --git a/keycloak-management-client/src/main/java/uk/ac/manchester/spinnaker/tools/GetDevId.java b/keycloak-management-client/src/main/java/uk/ac/manchester/spinnaker/tools/GetDevId.java index 56ad906426..62f468cbf7 100644 --- a/keycloak-management-client/src/main/java/uk/ac/manchester/spinnaker/tools/GetDevId.java +++ b/keycloak-management-client/src/main/java/uk/ac/manchester/spinnaker/tools/GetDevId.java @@ -88,6 +88,16 @@ public String getPass() { private Auth nextAuth; + /** + * Create an instance. + * + * @param databaseFile + * The database to store tokens and credentials in. + * @throws SQLException + * If database operations fail. + * @throws IOException + * If I/O operations fail. + */ public GetDevId(File databaseFile) throws SQLException, IOException { super(databaseFile); } @@ -248,8 +258,7 @@ public static void main(String... arguments) log.debug("response:\n{}", mapper.writeValueAsString(cr)); } catch (ClientRegistrationException e) { log.error("failed", e); - if (e.getCause() instanceof HttpErrorException) { - var ex = (HttpErrorException) e.getCause(); + if (e.getCause() instanceof HttpErrorException ex) { log.info("status: {}", ex.getStatusLine().getStatusCode()); log.info("response: {}", ex.getErrorResponse()); } diff --git a/pom.xml b/pom.xml index b48add9bc4..cc12a2cab7 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ limitations under the License. UTF-8 - 11 + 17 true true @@ -34,15 +34,15 @@ limitations under the License. 5.10.0 1.10.0 - 10.12.2 + 10.12.6 3.6.0 3.1.2 - 2.0.9 - 2.20.0 + 2.0.16 + 2.23.1 - 5.3.30 - 2.7.16 + 6.0.12 + 3.3.3 - 5.8.7 + 6.1.4 - 3.6.2 + 4.0.3 2.15.2 2.14 - 3.43.0.0 - 22.0.3 + 3.46.1.0 + 23.0.3 5.7.2 1.0.5 2.7.3 - 2.18.0 + 2.31.0 4.7.5 8.1.0 - 1.19.0 + 1.19.3 2.1.1 6.2.5.Final @@ -93,6 +93,13 @@ limitations under the License. pom import + + org.springframework.security + spring-security-bom + ${spring.security.version} + pom + import + org.testcontainers testcontainers-bom @@ -262,7 +269,7 @@ limitations under the License. commons-io commons-io - 2.13.0 + 2.15.1 org.apache.commons @@ -272,7 +279,7 @@ limitations under the License. commons-cli commons-cli - 1.5.0 + 1.9.0 org.apache.commons @@ -282,7 +289,7 @@ limitations under the License. org.apache.commons commons-text - 1.10.0 + 1.12.0 org.xerial @@ -305,14 +312,14 @@ limitations under the License. ${cxf.version} - org.springframework.security - spring-security-core - ${spring.security.version} + jakarta.servlet + jakarta.servlet-api + 6.0.0 - javax - javaee-api - 8.0.1 + jakarta.mail + jakarta.mail-api + 2.1.2 org.apache.cxf @@ -353,12 +360,12 @@ limitations under the License. org.java-websocket Java-WebSocket - 1.5.4 + 1.5.7 jakarta.validation jakarta.validation-api - 2.0.2 + 3.0.2 com.google.errorprone @@ -387,6 +394,11 @@ limitations under the License. 1.2.1 test + + org.hibernate.validator + hibernate-validator + 8.0.1.Final + org.rauschig jarchivelib @@ -442,7 +454,10 @@ limitations under the License. org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.13.0 + + true + org.apache.maven.plugins @@ -528,7 +543,7 @@ limitations under the License. org.apache.maven.plugins maven-checkstyle-plugin - 3.3.0 + 3.5.0 src/support/checkstyle/style.xml @@ -581,10 +596,8 @@ limitations under the License. https://docs.spring.io/spring-framework/docs/current/javadoc-api/ https://docs.spring.io/spring-security/site/docs/current/api/ https://docs.spring.io/spring-boot/docs/current/api/ - https://jakarta.ee/specifications/servlet/4.0/apidocs/ + https://cxf.apache.org/javadoc/latest-3.0.x/ https://errorprone.info/api/latest/ https://www.slf4j.org/api/ https://www.joda.org/joda-time/apidocs/ @@ -696,14 +709,14 @@ limitations under the License. 1.0 - org.apache.sling + io.leonard.maven.plugins jspc-maven-plugin - 2.3.4 + 4.1.1 org.apache.rat apache-rat-plugin - 0.15 + 0.16.1 @@ -873,6 +886,7 @@ limitations under the License. SpiNNaker-nmpimodel SpiNNaker-nmpiexec SpiNNaker-proxy + keycloak-management-client SpiNNaker Java Host Implementation of the host software for SpiNNaker in Java. @@ -1088,13 +1102,6 @@ limitations under the License. - - - KeycloakClient - - keycloak-management-client - - CheckForFIXME @@ -1132,33 +1139,6 @@ limitations under the License. - - Java17extras - - [17,) - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - - https://docs.spring.io/spring-framework/docs/current/javadoc-api/ - https://docs.spring.io/spring-security/site/docs/current/api/ - https://docs.spring.io/spring-boot/docs/current/api/ - https://jakarta.ee/specifications/servlet/4.0/apidocs/ - https://cxf.apache.org/javadoc/latest-3.0.x/ - https://errorprone.info/api/latest/ - https://www.slf4j.org/api/ - https://www.joda.org/joda-time/apidocs/ - - - - - - ErrorProne @@ -1180,6 +1160,8 @@ limitations under the License. documentation for getters, and its suggested replacement is terrible. --> -Xep:MissingSummary:OFF + + -Xep:StatementSwitchToExpressionSwitch:OFF @@ -1208,6 +1190,8 @@ limitations under the License. documentation for getters, and its suggested replacement is terrible. --> -Xep:MissingSummary:OFF + + -Xep:StatementSwitchToExpressionSwitch:OFF