Java tutorial
/* * Copyright 2015 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.web.servlet; import java.io.OutputStreamWriter; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.UriInfo; import org.glassfish.jersey.server.mvc.Viewable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.module.Module; import org.terasology.module.ModuleMetadata; import org.terasology.module.ModuleMetadataJsonAdapter; import org.terasology.module.RemoteModuleExtension; import org.terasology.naming.Name; import org.terasology.naming.Version; import org.terasology.naming.exception.VersionParseException; import org.terasology.web.version.VersionInfo; import org.terasology.web.model.ModuleListModel; import org.terasology.web.model.jenkins.Job; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multimap; import com.google.common.collect.TreeMultimap; import com.google.gson.stream.JsonWriter; /** * TODO Type description */ @Path("/modules/") public class ModuleServlet { private static final Logger logger = LoggerFactory.getLogger(ModuleServlet.class); private final ModuleListModel model; private final ModuleMetadataJsonAdapter metadataWriter; /** * Sorts modules descending by version - id is ignored */ private final Comparator<Module> versionComparator = (m1, m2) -> m2.getVersion().compareTo(m1.getVersion()); public ModuleServlet(ModuleListModel model) { this.model = model; this.metadataWriter = new ModuleMetadataJsonAdapter(); for (RemoteModuleExtension ext : RemoteModuleExtension.values()) { metadataWriter.registerExtension(ext.getKey(), ext.getValueType()); } } @GET @Path("list") @Produces(MediaType.APPLICATION_JSON) public Response list() { logger.info("Requested module list as json"); StreamingOutput stream = os -> { List<Name> sortedModuleIds = new ArrayList<>(model.getModuleIds()); sortedModuleIds.sort(null); try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) { writer.beginArray(); writer.setIndent(" "); // enable pretty printing for (Name name : sortedModuleIds) { for (Module module : model.getModuleVersions(name)) { ModuleMetadata meta = module.getMetadata(); metadataWriter.write(meta, writer); } } writer.endArray(); } }; return Response.ok(stream).build(); } @GET @Path("show") @Produces(MediaType.TEXT_HTML) public Viewable show() { logger.info("Requested module list as HTML"); Set<Name> names = model.getModuleIds(); // the key needs to be string, so that FreeMarker can use it for lookups Multimap<String, Module> map = TreeMultimap.create(String.CASE_INSENSITIVE_ORDER, versionComparator); for (Name name : names) { map.putAll(name.toString(), model.getModuleVersions(name)); } ImmutableMap<Object, Object> dataModel = ImmutableMap.builder().put("items", map.asMap()) .put("version", VersionInfo.getVersion()).build(); return new Viewable("/module-list.ftl", dataModel); } @GET @Path("list/latest") @Produces(MediaType.APPLICATION_JSON) public Response listLatest() { logger.info("Requested lastest info as json"); StreamingOutput stream = os -> { List<Name> sortedModuleIds = new ArrayList<>(model.getModuleIds()); sortedModuleIds.sort(null); try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) { writer.beginArray(); writer.setIndent(" "); // enable pretty printing for (Name name : sortedModuleIds) { Module module = model.getLatestModuleVersion(name); ModuleMetadata meta = module.getMetadata(); metadataWriter.write(meta, writer); } writer.endArray(); } }; return Response.ok(stream).build(); } @GET @Path("list/{module}") @Produces(MediaType.APPLICATION_JSON) public Response listModule(@PathParam("module") String moduleName) { logger.info("Requested module versions as json"); Name name = new Name(moduleName); StreamingOutput stream = os -> { try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) { writer.beginArray(); writer.setIndent(" "); // enable pretty printing for (Module module : model.getModuleVersions(name)) { ModuleMetadata meta = module.getMetadata(); metadataWriter.write(meta, writer); } writer.endArray(); } }; return Response.ok(stream).build(); } @GET @Path("show/{module}") @Produces(MediaType.TEXT_HTML) public Viewable showModule(@PathParam("module") String module) { logger.info("Requested module versions as HTML"); Name name = new Name(module); List<Module> sortedList = new ArrayList<>(model.getModuleVersions(name)); sortedList.sort(versionComparator); Map<String, Collection<Module>> map = Collections.singletonMap(module, sortedList); ImmutableMap<Object, Object> dataModel = ImmutableMap.builder().put("items", map).put("moduleId", module) .put("version", VersionInfo.getVersion()).build(); return new Viewable("/module-list.ftl", dataModel); } @GET @Path("list/{module}/latest") @Produces(MediaType.TEXT_HTML) public Response listModuleLatest(@Context UriInfo uriInfo, @PathParam("module") String module) { logger.info("Requested lastest module info as HTML"); int pathLen = uriInfo.getPath().length(); String path = uriInfo.getPath().substring(0, pathLen - "latest".length()); Module latest = model.getLatestModuleVersion(new Name(module)); if (latest == null) { return Response.status(Status.NOT_FOUND).build(); } String ver = latest.getVersion().toString(); URI redirect = URI.create(uriInfo.getBaseUri() + path + ver); return Response.temporaryRedirect(redirect).build(); } @GET @Path("list/{module}/{version}") @Produces(MediaType.APPLICATION_JSON) public Response listModuleVersion(@PathParam("module") String moduleName, @PathParam("version") String versionStr) { logger.info("Requested single module info as json"); try { Version version = new Version(versionStr); Module module = model.getModule(new Name(moduleName), version); if (module == null) { return Response.status(Status.NOT_FOUND).build(); } ModuleMetadata meta = module.getMetadata(); StreamingOutput stream = os -> { try (OutputStreamWriter writer = new OutputStreamWriter(os, StandardCharsets.UTF_8)) { metadataWriter.write(meta, writer); } }; return Response.ok(stream).build(); } catch (VersionParseException e) { logger.warn("Invalid version for module '{}' specified: {}", moduleName, versionStr); return Response.status(Status.NOT_FOUND).build(); } } @GET @Path("show/{module}/latest") @Produces(MediaType.TEXT_HTML) public Response showModuleLatest(@Context UriInfo uriInfo, @PathParam("module") String module) { logger.info("Requested lastest module info as HTML"); int pathLen = uriInfo.getPath().length(); String path = uriInfo.getPath().substring(0, pathLen - "latest".length()); Module latest = model.getLatestModuleVersion(new Name(module)); if (latest == null) { return Response.status(Status.NOT_FOUND).build(); } String ver = latest.getVersion().toString(); URI redirect = URI.create(uriInfo.getBaseUri() + path + ver); return Response.temporaryRedirect(redirect).build(); } @GET @Path("show/{module}/{version}") @Produces(MediaType.TEXT_HTML) public Response showModuleVersion(@PathParam("module") String module, @PathParam("version") String version) { logger.info("Requested module info as HTML"); try { Name moduleName = new Name(module); Version modVersion = new Version(version); Module mod = model.getModule(moduleName, modVersion); if (mod == null) { logger.warn("No entry for module '{}' found", module); return Response.status(Status.NOT_FOUND).build(); } ModuleMetadata meta = mod.getMetadata(); Set<Module> deps = model.resolve(moduleName, modVersion); ImmutableMap<Object, Object> dataModel = ImmutableMap.builder().put("meta", meta) .put("updated", RemoteModuleExtension.getLastUpdated(meta)) .put("downloadUrl", RemoteModuleExtension.getDownloadUrl(meta)) .put("downloadSize", RemoteModuleExtension.getArtifactSize(meta) / 1024) .put("dependencies", deps).put("version", VersionInfo.getVersion()).build(); return Response.ok(new Viewable("/module-info.ftl", dataModel)).build(); } catch (VersionParseException e) { logger.warn("Invalid version for module '{}' specified: {}", module, version); return Response.status(Status.NOT_FOUND).build(); } } @POST @Path("update") @Consumes(MediaType.APPLICATION_JSON) public Response updateModulePost(Job jobState) { String job = jobState.getName(); logger.info("Requested module update for {}", job); model.updateModule(new Name(job)); return Response.ok().build(); } @POST @Path("update-all") @Consumes(MediaType.APPLICATION_JSON) public Response updateAllModulesPost() { logger.info("Requested complete module update"); new Thread(model::updateAllModules).start(); return Response.ok().build(); } }