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

simplification of prevention of multiple starts #47

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 100 additions & 88 deletions src/main/java/fr/brouillard/oss/cssfx/CSSFX.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
* 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.
Expand All @@ -20,93 +20,100 @@
* #L%
*/

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.Callable;

import fr.brouillard.oss.cssfx.api.URIToPathConverter;
import fr.brouillard.oss.cssfx.impl.CSSFXMonitor;
import fr.brouillard.oss.cssfx.impl.URIToPathConverters;
import fr.brouillard.oss.cssfx.impl.log.CSSFXLogger;
import fr.brouillard.oss.cssfx.impl.log.CSSFXLogger.LogLevel;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Stage;

import javafx.stage.Window;
import fr.brouillard.oss.cssfx.api.URIToPathConverter;
import fr.brouillard.oss.cssfx.impl.CSSFXMonitor;
import fr.brouillard.oss.cssfx.impl.URIToPathConverters;
import fr.brouillard.oss.cssfx.impl.log.CSSFXLogger;
import fr.brouillard.oss.cssfx.impl.log.CSSFXLogger.LogLevel;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

public class CSSFX {
// prevent multiple global starts of CSSFX
private static boolean isCssFXStarted = false;
private static AtomicBoolean cssFXStarted = new AtomicBoolean(false);

/**
* @return Indicates if CSSFX is started or not.
*/
public static boolean isCSSFXStarted() {
return cssFXStarted.get();
}

/**
* Directly start monitoring the CSS of the application using defaults:
* <ul>
* <li>standard source file detectors: Maven, Gradle, execution from built JAR (details in {@link URIToPathConverters#DEFAULT_CONVERTERS})</li>
* <li>detection activated on all stages of the application, including the ones that will appear later on</li>
* </ul>
* </ul>
* @return a Runnable object to stop CSSFX monitoring
*/
synchronized public static Runnable start() {
if(!isCssFXStarted) {
isCssFXStarted = true;
return new CSSFXConfig().start();
} else {
return () -> {};
}
public static Runnable start() {
return start((config) -> {});
}

/**
* Directly start monitoring CSS for the given Window.
* <ul>
* <li>standard source file detectors: Maven, Gradle, execution from built JAR (details in {@link URIToPathConverters#DEFAULT_CONVERTERS})</li>
* <li>detection activated on the given Window only (and its children)</li>
* </ul>
* </ul>
* @param window the window that will be monitored
* @return a Runnable object to stop CSSFX monitoring
*/
public static Runnable start(Window window) {
CSSFXConfig cfg = new CSSFXConfig();
cfg.setRestrictedToWindow(window);
return cfg.start();
return start((config) -> config.setRestrictedToWindow(window));
}

/**
* Directly start monitoring CSS for the given Scene.
* <ul>
* <li>standard source file detectors: Maven, Gradle, execution from built JAR (details in {@link URIToPathConverters#DEFAULT_CONVERTERS})</li>
* <li>detection activated on the scene only (and its children)</li>
* </ul>
* </ul>
* @param scene the scene that will be monitored
* @return a Runnable object to stop CSSFX monitoring
*/
public static Runnable start(Scene scene) {
CSSFXConfig cfg = new CSSFXConfig();
cfg.setRestrictedToScene(scene);
return cfg.start();
return start((config) -> config.setRestrictedToScene(scene));
}

/**
* Directly start monitoring CSS for the given node.
* <ul>
* <li>standard source file detectors: Maven, Gradle, execution from built JAR (details in {@link URIToPathConverters#DEFAULT_CONVERTERS})</li>
* <li>detection activated on the node only (and its children)</li>
* </ul>
* </ul>
* @param node the node that will be monitored
* @return a Runnable object to stop CSSFX monitoring
*/
public static Runnable start(Node node) {
CSSFXConfig cfg = new CSSFXConfig();
cfg.setRestrictedToNode(node);
return cfg.start();
return start((config) -> config.setRestrictedToNode(node));
}

private static Runnable start(Consumer<CSSFXConfig> configAdapter) {
if(!isCSSFXStarted()) {
CSSFXConfig cfg = new CSSFXConfig();
configAdapter.accept(cfg);
return cfg.start();
} else {
CSSFXLogger.logger(CSSFX.class).warn("CSSFX was already started, only one start is allowed.");
return () -> {};
}
}

/**
* Restrict the source file detection for graphical sub-tree of the given {@link Stage}
*
*
* @param window the window to restrict the detection on, if null then no restriction will apply
* @return a {@link CSSFXConfig} object as a builder to allow further configuration
*/
Expand All @@ -117,7 +124,7 @@ public static CSSFXConfig onlyFor(Window window) {
}
/**
* Restrict the source file detection for graphical sub-tree of the given {@link Scene}
*
*
* @param s the scene to restrict the detection on, if null then no restriction will apply
* @return a {@link CSSFXConfig} object as a builder to allow further configuration
*/
Expand All @@ -128,7 +135,7 @@ public static CSSFXConfig onlyFor(Scene s) {
}
/**
* Restrict the source file detection for graphical sub-tree of the given {@link Node}
*
*
* @param n the node to restrict the detection on, if null then no restriction will apply
* @return a {@link CSSFXConfig} object as a builder to allow further configuration
*/
Expand All @@ -146,10 +153,10 @@ public static CSSFXConfig onlyFor(Node n) {
public static CSSFXConfig addConverter(URIToPathConverter converter) {
return new CSSFXConfig().addConverter(converter);
}

/**
* Stores information before finally building/starting the CSS monitoring.
*
*
* @author Matthieu Brouillard
*/
public static class CSSFXConfig {
Expand All @@ -158,7 +165,7 @@ public static class CSSFXConfig {
private Window restrictedToWindow = null;
private Scene restrictedToScene = null;
private Node restrictedToNode = null;

CSSFXConfig() {
}

Expand All @@ -173,7 +180,7 @@ void setRestrictedToScene(Scene restrictedToScene) {
void setRestrictedToNode(Node restrictedToNode) {
this.restrictedToNode = restrictedToNode;
}

/**
* Empty the list of default converters.
* Especially usefull for testing purposes where full control of the converters is required.
Expand All @@ -193,9 +200,9 @@ public CSSFXConfig addConverter(URIToPathConverter converter) {
converters.add(converter);
return this;
}

/**
* Start monitoring CSS resources with the config parameters collected until now.
* Start monitoring CSS resources with the config parameters collected until now.
* @return a Runnable object to stop CSSFX monitoring
*/
public Runnable start() {
Expand All @@ -204,53 +211,58 @@ public Runnable start() {
return () -> {};
}

if (!CSSFXLogger.isInitialized()) {
if (Boolean.getBoolean("cssfx.log")) {
LogLevel toActivate = LogLevel.INFO;
String levelStr = System.getProperty("cssfx.log.level", "INFO");
try {
toActivate = LogLevel.valueOf(levelStr);
} catch (Exception ignore) {
System.err.println("[CSSFX] invalid value for cssfx.log.level, '" + levelStr + "' is not allowed. Select one in: " + Arrays.asList(LogLevel.values()));
}
CSSFXLogger.setLogLevel(toActivate);

String logType = System.getProperty("cssfx.log.type", "console");
switch (logType) {
case "noop":
if (cssFXStarted.compareAndSet(false, true)) {
if (!CSSFXLogger.isInitialized()) {
if (Boolean.getBoolean("cssfx.log")) {
LogLevel toActivate = LogLevel.INFO;
String levelStr = System.getProperty("cssfx.log.level", "INFO");
try {
toActivate = LogLevel.valueOf(levelStr);
} catch (Exception ignore) {
System.err.println("[CSSFX] invalid value for cssfx.log.level, '" + levelStr + "' is not allowed. Select one in: " + Arrays.asList(LogLevel.values()));
}
CSSFXLogger.setLogLevel(toActivate);

String logType = System.getProperty("cssfx.log.type", "console");
switch (logType) {
case "noop":
CSSFXLogger.noop();
break;
case "console":
CSSFXLogger.console();
break;
case "jul":
CSSFXLogger.jul();
break;
default:
System.err.println("[CSSFX] invalid value for cssfx.log.type, '" + logType + "' is not allowed. Select one in: " + Arrays.asList("noop", "console", "jul"));
break;
}
} else {
CSSFXLogger.noop();
break;
case "console":
CSSFXLogger.console();
break;
case "jul":
CSSFXLogger.jul();
break;
default:
System.err.println("[CSSFX] invalid value for cssfx.log.type, '" + logType + "' is not allowed. Select one in: " + Arrays.asList("noop", "console", "jul"));
break;
}
}

CSSFXMonitor m = new CSSFXMonitor();

if (restrictedToWindow != null) {
m.setWindows(FXCollections.singletonObservableList(restrictedToWindow));
} else if (restrictedToScene != null) {
m.setScenes(FXCollections.singletonObservableList(restrictedToScene));
} else if (restrictedToNode != null) {
m.setNodes(FXCollections.singletonObservableList(restrictedToNode));
} else {
CSSFXLogger.noop();
// we monitor all the stages
// THIS CANNOT WORK!
ObservableList<Window> monitoredStages = (restrictedToWindow == null)?Window.getWindows():FXCollections.singletonObservableList(restrictedToWindow);
m.setWindows(monitoredStages);
}
}

CSSFXMonitor m = new CSSFXMonitor();

if (restrictedToWindow != null) {
m.setWindows(FXCollections.singletonObservableList(restrictedToWindow));
} else if (restrictedToScene != null) {
m.setScenes(FXCollections.singletonObservableList(restrictedToScene));
} else if (restrictedToNode != null) {
m.setNodes(FXCollections.singletonObservableList(restrictedToNode));

return start(() -> m);
} else {
// we monitor all the stages
// THIS CANNOT WORK!
ObservableList<Window> monitoredStages = (restrictedToWindow == null)?Window.getWindows():FXCollections.singletonObservableList(restrictedToWindow);
m.setWindows(monitoredStages);
CSSFXLogger.logger(CSSFX.class).warn("CSSFX was already started, starting a new CSSFXConfig is not allowed.");
return () -> {};
}

return start(() -> m);
}

private Runnable start(Callable<CSSFXMonitor> monitorBuilder) {
Expand Down
21 changes: 14 additions & 7 deletions src/test/java/fr/brouillard/oss/cssfx/test/CSSFXTesterApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@
* #L%
*/

import java.util.Random;

import fr.brouillard.oss.cssfx.CSSFX;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventType;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
Expand All @@ -35,9 +33,9 @@
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

import javafx.stage.WindowEvent;
import fr.brouillard.oss.cssfx.CSSFX;

import java.util.Random;

public class CSSFXTesterApp extends Application {
private Button btnLoadOddCSS; // needed as field for tests purposes
Expand All @@ -48,6 +46,9 @@ public void start(Stage stage) throws Exception {
stage.show();
Runnable cssfxCloseAction = CSSFX.start();

// demo second start show a warning
CSSFX.start();

stage.getScene().getWindow()
.addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, (event) -> cssfxCloseAction.run());
}
Expand Down Expand Up @@ -78,11 +79,17 @@ private Node createButtonBar() {
System.out.println("Forcing a GC");
System.gc();
});


Button startCSSFXAction = new Button("Start CSSFX");
startCSSFXAction.addEventHandler(ActionEvent.ACTION, e -> {
System.out.println("Click on Start CSSFX");
CSSFX.start();
});

Button fakeAction = new Button("Action");
fakeAction.addEventHandler(ActionEvent.ACTION, e -> System.out.println("You clicked the fake action button"));

fp.getChildren().addAll(gcAction, fakeAction);
fp.getChildren().addAll(gcAction, fakeAction, startCSSFXAction);
String buttonBarCSSUri = getClass().getResource("bottom.css").toExternalForm();
fp.getStylesheets().add(buttonBarCSSUri);

Expand Down