Skip to content

Commit

Permalink
Added systray icon
Browse files Browse the repository at this point in the history
  • Loading branch information
therealryan committed Aug 17, 2024
1 parent 7afecaf commit 5dd18a7
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 15 deletions.
27 changes: 24 additions & 3 deletions app/src/main/java/dev/flowty/bowlby/app/Main.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package dev.flowty.bowlby.app;

import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.function.Consumer;

import dev.flowty.bowlby.app.cfg.Parameters;
import dev.flowty.bowlby.app.github.Artifacts;
import dev.flowty.bowlby.app.github.GithubApiClient;
import dev.flowty.bowlby.app.srv.Server;
import dev.flowty.bowlby.app.ui.Gui;

/**
* Application entrypoint
Expand All @@ -29,6 +32,7 @@ public static void main( String... args ) {
}

private final Server server;
private final Gui gui;

/**
* @param parameters Configuration object
Expand All @@ -47,26 +51,43 @@ public Main( Parameters parameters ) {
ghClient,
artifacts,
parameters.latestArtifactCacheDuration() );
gui = new Gui( this, parameters.iconBehaviour() );
}

/**
* Starts the application
*/
public void start() {
server.start();
gui.start();
}

/**
* @return The address that the server is listening on
*/
public InetSocketAddress address() {
return server.address();
public URI uri() {
try {
return new URI( "http:/" + server.address() );
}
catch( URISyntaxException e ) {
throw new IllegalStateException( "Bad address", e );
}
}

/**
* Adds an activity listener
*
* @param listener the object to be appraised of request-handling activity
*/
public void withListener( Consumer<Boolean> listener ) {
server.withListener( listener );
}

/**
* Stops the application
*/
public void stop() {
server.stop();
gui.stop();
}
}
17 changes: 17 additions & 0 deletions app/src/main/java/dev/flowty/bowlby/app/cfg/Parameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.stream.Stream;

import dev.flowty.bowlby.app.github.Entity.Repository;
import dev.flowty.bowlby.app.ui.Gui.IconBehaviour;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
Expand Down Expand Up @@ -79,6 +80,15 @@ public class Parameters {
.ofNullable( System.getenv( "BOWLBY_ARTIFACT_VALIDITY" ) )
.orElse( "P3D" );

@Option(names = { "-i", "--icon" },
description = """
Controls the system tray icon. Choose from NONE, STATIC or DYNAMIC.
The dynamic icon will give a visible indication of request-handling activity
Overrides environment variable 'BOWLBY_ICON'""")
private String iconBehaviour = Optional
.ofNullable( System.getenv( "BOWLBY_ICON" ) )
.orElse( "DYNAMIC" );

private static final Pattern REPO_RGX = Pattern.compile( "(\\w+)/(\\w+)" );
private final Set<Repository> repos;

Expand Down Expand Up @@ -159,4 +169,11 @@ public Duration latestArtifactCacheDuration() {
public Duration artifactCacheDuration() {
return Duration.parse( artifactValidity );
}

/**
* @return The desired icon behaviour
*/
public IconBehaviour iconBehaviour() {
return IconBehaviour.from( iconBehaviour );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public Path getArtifact( Artifact artifact, Path destination ) {

Optional<String> dlUri = redirect.headers().firstValue( "location" );
if( redirect.statusCode() != 302 && dlUri.isEmpty() ) {
LOG.warn( "Failed to get download URL {}/{}",
LOG.error( "Failed to get download URL {}/{}",
redirect.statusCode(), redirect.body() );
}
else {
Expand Down Expand Up @@ -156,6 +156,13 @@ public Run getLatestRun( Workflow workflow, Branch branch ) {
.build(),
ListWorkflowRunResponse.HANDLER );

if( response.statusCode() != 200 ) {
LOG.error( "Unexpected response status {}. Run with {} to see the full response.",
response.statusCode(),
"-Dorg.slf4j.simpleLogger.defaultLogLevel=trace" );
return null;
}

return Optional.ofNullable( response )
.map( HttpResponse::body )
.map( b -> b.runs )
Expand Down Expand Up @@ -191,6 +198,13 @@ public Set<NamedArtifact> getArtifacts( Run run ) {
.build(),
ListWorkflowRunArtifactsResponse.HANDLER );

if( response.statusCode() != 200 ) {
LOG.error( "Unexpected response status {}. Run with {} to see the full response.",
response.statusCode(),
"-Dorg.slf4j.simpleLogger.defaultLogLevel=trace" );
return null;
}

return Optional.ofNullable( response )
.map( HttpResponse::body )
.map( body -> body.artifacts )
Expand Down Expand Up @@ -228,6 +242,13 @@ public Branch getDefaultBranch( Repository repo ) {
.build(),
GetRepoResponse.HANDLER );

if( response.statusCode() != 200 ) {
LOG.error( "Unexpected response status {}. Run with {} to see the full response.",
response.statusCode(),
"-Dorg.slf4j.simpleLogger.defaultLogLevel=trace" );
return null;
}

return new Branch( repo, response.body().defaultBranch );
}
catch( IOException | InterruptedException | URISyntaxException e ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ private LatestArtifact getLatest( Workflow workflow ) {
}
else {
Branch defaultBranch = client.getDefaultBranch( workflow.repo() );
if( defaultBranch == null ) {
return null;
}
Run latest = client.getLatestRun( workflow, defaultBranch );
if( latest == null ) {
return null;
Expand Down
25 changes: 19 additions & 6 deletions app/src/main/java/dev/flowty/bowlby/app/srv/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.time.Duration;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.function.Consumer;

import org.slf4j.Logger;

Expand All @@ -21,6 +22,7 @@
public class Server {
private static final Logger LOG = org.slf4j.LoggerFactory.getLogger( Server.class );
private final HttpServer server;
private final ServerListeners listeners = new ServerListeners();

/**
* Creates a new server
Expand All @@ -38,12 +40,14 @@ public Server( int port, Set<Repository> repos, GithubApiClient ghClient, Artifa
try {
server = HttpServer.create( new InetSocketAddress( port ), 0 );
server.setExecutor( Executors.newCachedThreadPool() );
server.createContext( "/favicon.ico", new ResourceHandler(
"/favicon.ico", "image/vnd.microsoft.icon" ) );
server.createContext( "/artifacts", new ArtifactHandler( repos, artifacts ) );
server.createContext( "/latest",
new LatestArtifactHandler( repos, ghClient, latestArtifactCacheDuration ) );
server.createContext( "/", new LinkHandler() );
server.createContext( "/favicon.ico", listeners.wrap(
new ResourceHandler( "/favicon.ico", "image/vnd.microsoft.icon" ) ) );
server.createContext( "/artifacts", listeners.wrap(
new ArtifactHandler( repos, artifacts ) ) );
server.createContext( "/latest", listeners.wrap(
new LatestArtifactHandler( repos, ghClient, latestArtifactCacheDuration ) ) );
server.createContext( "/", listeners.wrap(
new LinkHandler() ) );
}
catch( IOException ioe ) {
throw new UncheckedIOException( "Failed to create server", ioe );
Expand All @@ -58,6 +62,15 @@ public void start() {
LOG.info( "started at http:/{}", server.getAddress() );
}

/**
* Adds an activity listener
*
* @param listener the object to be appraised of request-handling activity
*/
public void withListener( Consumer<Boolean> listener ) {
listeners.with( listener );
}

/**
* @return The address that the server is listening on
*/
Expand Down
60 changes: 60 additions & 0 deletions app/src/main/java/dev/flowty/bowlby/app/srv/ServerListeners.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package dev.flowty.bowlby.app.srv;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import com.sun.net.httpserver.HttpHandler;

/**
* Mechanism for behaviours that are interested in server activity
*/
public class ServerListeners {

private final List<Consumer<Boolean>> listeners = new ArrayList<>();

/**
* Wraps a handler in activity-notification behaviour
*
* @param handler The handler
* @return a handler that will notify listeners on behaviour
*/
public HttpHandler wrap( HttpHandler handler ) {
return exchange -> {
try( Notifying n = notifying() ) {
handler.handle( exchange );
}
};
}

/**
* @param listener will be supplied with <code>true</code> when request handling
* begins starts, and <code>false</code> when it ends
* @return <code>this</code>
*/
public ServerListeners with( Consumer<Boolean> listener ) {
listeners.add( listener );
return this;
}

/**
* @return an {@link AutoCloseable} that handles listener notification
*/
private Notifying notifying() {
return new Notifying();
}

/**
* Handles listener notification
*/
private class Notifying implements AutoCloseable {
private Notifying() {
listeners.forEach( l -> l.accept( true ) );
}

@Override
public void close() {
listeners.forEach( l -> l.accept( false ) );
}
}
}
Loading

0 comments on commit 5dd18a7

Please sign in to comment.