2222
2323import java .io .File ;
2424import java .io .IOException ;
25+ import java .nio .file .Files ;
2526import java .nio .file .Path ;
2627import java .nio .file .Paths ;
28+ import java .nio .file .StandardWatchEventKinds ;
2729import java .nio .file .WatchEvent .Kind ;
2830import java .util .List ;
31+ import java .util .Map ;
32+ import java .util .concurrent .ConcurrentHashMap ;
2933import java .util .concurrent .atomic .AtomicBoolean ;
30- import java .util .concurrent .atomic .AtomicReference ;
3134
35+ import org .jooby .MediaType ;
3236import org .jooby .Request ;
3337import org .jooby .Response ;
3438import org .jooby .Route ;
3539import org .jooby .assets .AssetCompiler ;
3640import org .jooby .assets .AssetException ;
3741import org .jooby .assets .AssetProblem ;
42+ import org .jooby .internal .URLAsset ;
3843
3944import com .google .common .collect .Lists ;
4045import com .typesafe .config .Config ;
4146
47+ import javaslang .control .Try ;
48+
4249public class LiveCompiler implements Route .Handler {
4350
4451 private final Config conf ;
4552
4653 private final AssetCompiler compiler ;
4754
48- private final AtomicReference < AssetException > lastErr = new AtomicReference < AssetException >( null );
55+ private final Map < String , AssetException > errors = new ConcurrentHashMap <>( );
4956
5057 private final Watcher watcher ;
5158
5259 private final AtomicBoolean firstRun = new AtomicBoolean (true );
5360
61+ private Path basedir ;
62+
5463 public LiveCompiler (final Config conf , final AssetCompiler compiler ) throws IOException {
5564 this .conf = requireNonNull (conf , "Config is required." );
5665 this .compiler = requireNonNull (compiler , "Asset compiler is required." );
57- this .watcher = new Watcher (this ::onChange , Paths .get ("public" ));
66+ this .basedir = Paths .get ("public" );
67+ this .watcher = new Watcher (this ::onChange , basedir );
68+
5869 }
5970
6071 private void onChange (final Kind <?> kind , final Path path ) {
6172 File outputdir = new File (conf .getString ("application.tmpdir" ), "__public_" );
6273 outputdir .mkdirs ();
74+ String filename = Route .normalize (Try .of (() -> path .subpath (1 , path .getNameCount ()).toString ())
75+ .getOrElse (path .toString ()));
6376 try {
6477 boolean firstRun = this .firstRun .compareAndSet (true , false );
65- if (!firstRun ) {
78+ if (!firstRun && ! path . equals ( basedir ) ) {
6679 if (!compiler .contains (path .toString ())) {
6780 return ;
6881 }
6982 }
70- compiler .build (conf .getString ("application.env" ), outputdir );
71- lastErr .set (null );
83+ if (kind == StandardWatchEventKinds .ENTRY_DELETE ) {
84+ errors .remove (filename );
85+ } else {
86+ if (Files .isDirectory (path )) {
87+ errors .clear ();
88+ compiler .build (conf .getString ("application.env" ), outputdir );
89+ } else {
90+ MediaType type = MediaType .byPath (path ).orElse (MediaType .octetstream );
91+ compiler .build (new URLAsset (path .toUri ().toURL (), filename , type ));
92+ errors .remove (filename );
93+ }
94+ }
7295 } catch (AssetException ex ) {
73- lastErr .set (rewrite (ex ));
96+ String localname = ex .getProblems ().stream ()
97+ .findFirst ()
98+ .map (AssetProblem ::getFilename )
99+ .orElse (filename );
100+ errors .put (localname , rewrite (ex ));
74101 } catch (Exception ex ) {
75- ex .printStackTrace ();
76102 AssetException assetEx = rewrite (new AssetException ("compiler" ,
77- new AssetProblem (path . toString () , -1 , -1 , ex .getMessage (), null ), ex ));
78- lastErr . set ( assetEx );
103+ new AssetProblem (filename , -1 , -1 , ex .getMessage (), null ), ex ));
104+ errors . put ( filename , assetEx );
79105 }
80106 }
81107
@@ -97,9 +123,11 @@ private AssetException rewrite(final AssetException ex) {
97123
98124 @ Override
99125 public void handle (final Request req , final Response rsp ) throws Throwable {
100- AssetException ex = lastErr .get ();
101- if (ex != null ) {
102- throw ex ;
126+ if (req .param ("assets.sync" ).isSet ()) {
127+ onChange (StandardWatchEventKinds .ENTRY_MODIFY , basedir );
128+ }
129+ if (errors .size () > 0 ) {
130+ throw errors .values ().iterator ().next ();
103131 }
104132 }
105133
0 commit comments