Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgraded Jersey and Jetty #2405

Merged
merged 1 commit into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
<changelist>-SNAPSHOT</changelist>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jetty.version>9.4.41.v20210516</jetty.version>
<jersey.version>2.29.1</jersey.version>
<jetty.version>12.0.3</jetty.version>
<jersey.version>3.1.5</jersey.version>
<gluegen.version>2.5.0</gluegen.version>
<jogl.version>2.5.0</jogl.version>
<miglayout.version>3.7.4</miglayout.version>
Expand Down
37 changes: 28 additions & 9 deletions ugs-pendant/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,24 @@
<version>${commons-lang3.version}</version>
</dependency>

<!-- Javax APIS -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty.version}</version>
<groupId>jakarta.websocket</groupId>
<artifactId>jakarta.websocket-api</artifactId>
<version>2.0.0</version>
</dependency>

<!-- Jersey + Jetty Server -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
Expand Down Expand Up @@ -73,6 +75,23 @@
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
</dependency>

<!-- For WebSocket -->
<dependency>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-jakarta-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10</groupId>
<artifactId>jetty-ee10-servlet</artifactId>
<version>${jetty.version}</version>
</dependency>
</dependencies>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Will Winder
Copyright 2016-2023 Will Winder

This file is part of Universal Gcode Sender (UGS).

Expand All @@ -21,24 +21,16 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.universalgcodesender.model.BackendAPI;

/**
* A factory for accessing the backend in injected resources
* A provider for accessing the backend in injected resources
*/
public class BackendAPIFactory {
private static BackendAPIFactory instance;
private BackendAPI backendAPI;

public static BackendAPIFactory getInstance() {
if (instance == null) {
instance = new BackendAPIFactory();
}
return instance;
}
public class BackendProvider {
private static BackendAPI backendAPI;

public void register(BackendAPI backendAPI) {
this.backendAPI = backendAPI;
public static void register(BackendAPI backendAPI) {
BackendProvider.backendAPI = backendAPI;
}

public BackendAPI getBackendAPI() {
public static BackendAPI getBackendAPI() {
return backendAPI;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Will Winder
Copyright 2016-2023 Will Winder

This file is part of Universal Gcode Sender (UGS).

Expand All @@ -20,11 +20,11 @@ This file is part of Universal Gcode Sender (UGS).

import com.willwinder.universalgcodesender.pendantui.v1.model.PendantError;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

public class ExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<Exception> {
public class ExceptionMapper implements jakarta.ws.rs.ext.ExceptionMapper<Exception> {

@Override
public Response toResponse(Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Will Winder
Copyright 2016-2023 Will Winder

This file is part of Universal Gcode Sender (UGS).

Expand All @@ -22,24 +22,27 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.universalgcodesender.model.BackendAPI;
import com.willwinder.universalgcodesender.model.UGSEvent;
import com.willwinder.universalgcodesender.model.events.SettingChangedEvent;
import com.willwinder.universalgcodesender.pendantui.html.StaticConfig;
import com.willwinder.universalgcodesender.pendantui.v1.AppV1Config;
import com.willwinder.universalgcodesender.pendantui.v1.ws.EventsSocket;
import com.willwinder.universalgcodesender.services.JogService;
import jakarta.ws.rs.core.UriBuilder;
import net.glxn.qrgen.QRCode;
import net.glxn.qrgen.image.ImageType;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;

import java.io.ByteArrayOutputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URL;
import java.net.URI;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
Expand All @@ -51,55 +54,37 @@ This file is part of Universal Gcode Sender (UGS).
* @author bobj
*/
public class PendantUI implements UGSEventListener {
private BackendAPI backendAPI;
private Server server = null;
private int port = 8080;
public static final String WEBSOCKET_CONTEXT_PATH = "/ws/v1";
public static final String API_CONTEXT_PATH = "/api/v1";
private static final Logger LOG = Logger.getLogger(PendantUI.class.getSimpleName());
private final JogService jogService;
private final BackendAPI backendAPI;
private int port = 8080;
private Server server;

public PendantUI(BackendAPI backendAPI) {
this.backendAPI = backendAPI;
backendAPI.addUGSEventListener(this);
BackendAPIFactory.getInstance().register(backendAPI);
jogService = new JogService(backendAPI);
BackendProvider.register(backendAPI);
}

public Resource getBaseResource(String directory) {
try {
URL res = getClass().getResource(directory);
return Resource.newResource(res);
} catch (Exception e) {
throw new RuntimeException(e);
}
}


/**
* Launches the local web server.
*
* @return the url for the pendant interface
*/
public List<PendantURLBean> start() {
int port = backendAPI.getSettings().getPendantPort();
server = new Server(port);
port = backendAPI.getSettings().getPendantPort();
URI baseUri = UriBuilder.fromUri("http://localhost/").port(port).build();

ResourceHandler staticResourceHandler = new ResourceHandler();
staticResourceHandler.setDirectoriesListed(true);
staticResourceHandler.setWelcomeFiles(new String[]{"index.html"});
staticResourceHandler.setBaseResource(getBaseResource("/resources/ugs-pendant"));
server = JettyHttpContainerFactory.createServer(baseUri, false);
ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection();
server.setHandler(contextHandlerCollection);

ContextHandler staticResourceHandlerContext = new ContextHandler();
staticResourceHandlerContext.setContextPath("/");
staticResourceHandlerContext.setHandler(staticResourceHandler);

// Create a servlet servletContextHandler
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
servletContextHandler.setContextPath("/api");
ServletHolder servletHolder = servletContextHandler.addServlet(ServletContainer.class, "/*");
servletHolder.setInitOrder(1);
servletHolder.setInitParameter("javax.ws.rs.Application", AppConfig.class.getCanonicalName());

HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]{servletContextHandler, staticResourceHandlerContext, new DefaultHandler()});
server.setHandler(handlers);
contextHandlerCollection.addHandler(createResourceConfigHandler(new StaticConfig(), ""));
contextHandlerCollection.addHandler(createResourceConfigHandler(new AppV1Config(backendAPI, jogService), API_CONTEXT_PATH));
contextHandlerCollection.addHandler(createWebSocketHandler(WEBSOCKET_CONTEXT_PATH));

try {
server.start();
Expand All @@ -110,6 +95,25 @@ public List<PendantURLBean> start() {
return getUrlList();
}

private ServletContextHandler createResourceConfigHandler(ResourceConfig config, String path) {
ServletContainer servletContainer = new ServletContainer(config);
ServletHolder servletHolder = new ServletHolder(servletContainer);
ServletContextHandler servletContextHandler = new ServletContextHandler();
servletContextHandler.setContextPath(path);
servletContextHandler.addServlet(servletHolder, "/*");
return servletContextHandler;
}

private ServletContextHandler createWebSocketHandler(String contextPath) {
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath(contextPath);
JakartaWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) -> {
wsContainer.setDefaultMaxTextMessageBufferSize(65535);
wsContainer.addEndpoint(EventsSocket.class);
});
return context;
}

/**
* Unfortunately, this is not as simple as it seems... since you can have multiple addresses and some of those may not be available via wireless
*
Expand All @@ -131,8 +135,7 @@ public List<PendantURLBean> getUrlList() {
while (addressEnum.hasMoreElements()) {
InetAddress addr = addressEnum.nextElement();
String hostAddress = addr.getHostAddress();
if (!hostAddress.contains(":") &&
!hostAddress.equals("127.0.0.1")) {
if (!hostAddress.contains(":") && !hostAddress.equals("127.0.0.1")) {
String url = "http://" + hostAddress + ":" + port;
ByteArrayOutputStream bout = QRCode.from(url).to(ImageType.PNG).stream();
out.add(new PendantURLBean(url, bout.toByteArray()));
Expand Down Expand Up @@ -162,10 +165,6 @@ public BackendAPI getBackendAPI() {
return backendAPI;
}

public void setBackendAPI(BackendAPI backendAPI) {
this.backendAPI = backendAPI;
}

@Override
public void UGSEvent(UGSEvent evt) {
if (evt instanceof SettingChangedEvent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright 2023 Will Winder

This file is part of Universal Gcode Sender (UGS).

UGS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

UGS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.universalgcodesender.pendantui.html;

import org.glassfish.jersey.server.ResourceConfig;

public class StaticConfig extends ResourceConfig {
public StaticConfig() {
register(StaticResource.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
Copyright 2023 Will Winder

This file is part of Universal Gcode Sender (UGS).

UGS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

UGS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.universalgcodesender.pendantui.html;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
import static jakarta.ws.rs.core.Response.Status.NOT_FOUND;

import java.io.InputStream;
import java.util.Objects;

@Path("/")
public class StaticResource {

public static final String RESOURCES_PATH = "/resources/ugs-pendant/%s";

private static String getMimeType(String path) {
String mimeType = "application/octet-stream";
if (path.endsWith(".js")) {
mimeType = "text/javascript";
} else if (path.endsWith(".html")) {
mimeType = "text/html";
} else if (path.endsWith(".css")) {
mimeType = "text/css";
}
return mimeType;
}

@GET
public Response getIndex() throws Exception {
return getStaticResource("");
}

@GET
@Path("{path:.*\\.(jpg|gif|html|js|css|ico)$}")
public Response getStaticResource(@PathParam("path") String path) throws Exception {
if (path.equalsIgnoreCase("")) {
path = "index.html";
}

InputStream resource = StaticResource.class.getResourceAsStream(String.format(RESOURCES_PATH, path));
String mimeType = getMimeType(path);
return Objects.isNull(resource) ? Response.status(NOT_FOUND).build() :
Response.ok().entity(resource).header(HttpHeaders.CONTENT_TYPE, mimeType).build();
}
}