package act.app;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* 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.
* #L%
*/
import act.Act;
import org.osgl.$;
import org.osgl.Osgl;
import org.osgl.exception.NotAppliedException;
import org.osgl.util.E;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import static act.app.ProjectLayout.Utils.file;
import static act.app.RuntimeDirs.CLASSES;
import static act.app.RuntimeDirs.CONF;
import static act.route.RouteTableRouterBuilder.ROUTES_FILE;
/**
* Defines the project file structure supported by Act.
* <p>Used ONLY at dev time for Act to decide where to pick up
* the application srccode files, configuration files etc</p>
*/
public interface ProjectLayout {
/**
* The className of the properties file sit directly in {@code appBase} dir that
* specifies the project layout of the application.
* <p>This properties file is only required if the app project uses a
* customized project layout, i.e. not one of the
* {@link ProjectLayout.PredefinedLayout predefined layouts}:
* </p>
* <ul>
* <li>{@link ProjectLayout.PredefinedLayout#MAVEN maven} or</li>
* <li>{@link ProjectLayout.PredefinedLayout#PLAY play}</li>
* </ul>
*/
String PROJ_LAYOUT_FILE = "proj.layout";
enum PredefinedLayout implements ProjectLayout {
/**
* The standard maven style project layout looks like:
* <pre>
* /<app_root>
* | --- src/
* | --- main/
* | --- java/
* | --- ... # your java packages
* | --- lib/
* | --- ... # your lib folder (contains jar files)
* | --- resources/
* | --- app.conf # the application configuration file
* | --- routes # the routing table
* | --- ... # other resources
* | --- test/
* | --- java/
* | --- ... your test packages
* | --- resources/
* | --- ...
* | --- target/ # the build target folder
* </pre>
* <b>{@code MAVEN} layout is used only when Act running in {@link Act.Mode#DEV dev mode}</b>
*/
MAVEN() {
@Override
public File source(File appBase) {
return file(appBase, "src/main/java");
}
@Override
public File testSource(File appBase) {
return file(appBase, "src/test/java");
}
@Override
public File resource(File appBase) {
String resources = Act.isDev() ? "src/main/resources" : "classes";
return file(appBase, resources);
}
@Override
public File testResource(File appBase) {
String resources = Act.isDev() ? "src/test/resources" : "test-classes";
return file(appBase, resources);
}
@Override
public File lib(File appBase) {
String lib = Act.isDev() ? "src/main/lib" : "lib";
return file(appBase, lib);
}
@Override
public File testLib(File appBase) {
String lib = Act.isDev() ? "src/test/lib" : "test-lib";
return file(appBase, lib);
}
@Override
public File asset(File appBase) {
String asset = Act.isDev() ? "src/main/asset" : "asset";
return file(appBase, asset);
}
@Override
public File target(File appBase) {
return file(appBase, "target");
}
},
PKG() {
@Override
public File source(File appBase) {
return null;
}
@Override
public File testSource(File appBase) {
return null;
}
@Override
public File testResource(File appBase) {
return null;
}
@Override
public File testLib(File appBase) {
return null;
}
@Override
public File resource(File appBase) {
return file(appBase, "classes");
}
@Override
public File lib(File appBase) {
return file(appBase, "lib");
}
@Override
public File asset(File appBase) {
return file(appBase, "asset");
}
@Override
public File target(File appBase) {
return appBase;
}
},
/**
* The Playframework v1.x project layout, looks like:
* <pre>
* /<app_root>
* | --- app/
* | --- # your java packages
* | --- lib/
* | --- # your lib
* | --- conf/
* | --- app.conf # the application configuration file
* | --- routes # the routing table
* | --- ... # other resources
* | --- test/
* | --- # your test packages
* | --- tmp/ # the build target folder
* </pre>
* <b>{@code PLAY} layout is used only when Act running in {@link Act.Mode#DEV dev mode}</b>
*/
PLAY() {
@Override
public File source(File appBase) {
return file(appBase, "app");
}
@Override
public File testSource(File appBase) {
return file(appBase, "test");
}
@Override
public File resource(File appBase) {
return file(appBase, "conf");
}
@Override
public File testResource(File appBase) {
return resource(appBase);
}
@Override
public File lib(File appBase) {
return file(appBase, "lib");
}
@Override
public File testLib(File appBase) {
return lib(appBase);
}
@Override
public File asset(File appBase) {
return file(appBase, "public");
}
@Override
public File target(File appBase) {
return file(appBase, "tmp");
}
},
;
@Override
public File conf(File appBase) {
File confBase = Act.isDev() ? resource(appBase) : new File(appBase, CLASSES);
File file = new File(confBase, CONF);
return file.exists() ? file : confBase;
}
@Override
public List<File> routeTables(File appBase) {
List<File> files = new ArrayList<>();
File resourceBase = resource(appBase);
files.add(file(resourceBase, ROUTES_FILE));
File confBase = conf(appBase);
files.add(file(confBase, ROUTES_FILE));
File commonBase = file(confBase, "common");
files.add(file(commonBase, ROUTES_FILE));
File profileBase = file(confBase, Act.profile());
files.add(file(profileBase, ROUTES_FILE));
return files;
}
public static ProjectLayout valueOfIgnoreCase(String s) {
s = s.trim().toUpperCase();
if (MAVEN.name().equals(s)) return MAVEN;
if (PLAY.name().equals(s)) return PLAY;
return null;
}
}
/**
* Returns Java srccode file root in relation to the
* {@code appBase}
*/
File source(File appBase);
/**
* Returns Java source code file root in test scope in relation
* to the {@code appBase}
* @param appBase
* @return
*/
File testSource(File appBase);
/**
* Returns Resource files root in relation to the
* {@code appBase} specified
*
* @param appBase
* @return
*/
File resource(File appBase);
/**
* Returns Resource files root in test scope in relation to the
* {@code appBase} specified
*
* @param appBase
* @return
*/
File testResource(File appBase);
/**
* Returns lib folder which contains arbitrary jar files in relation to the
* {@code appBase} specified
*/
File lib(File appBase);
/**
* Returns lib folder which contains arbitrary jar files in relation to the
* {@code appBase} specified in test scope
*/
File testLib(File appBase);
/**
* Returns asset folder which contains public accessible files like js/css/img etc
* in relation to the {@code appBase} specified
*/
File asset(File appBase);
/**
* Returns the build target folder in relation to the
* {@code appBase} specified
*/
File target(File appBase);
/**
* Returns the routing table file in relation to the
* {@code appBase} specified
*/
List<File> routeTables(File appBase);
/**
* Returns the app configuration location in relation to the
* {@code appBase} specified.
* <p>The configuration location could be either a File or
* a directory that contains a list of properties files or
* contains sub directories of a list of properties files</p>
*/
File conf(File appBase);
enum util {
;
/**
* check if a dir is application base as per given project layout
*
* @param dir the folder to be probed
* @param layout the project layout used to probe the folder
* @return {@code true if the folder is app base as per given project layout}
*/
public static boolean probeAppBase(File dir, ProjectLayout layout) {
// try conf file
File conf = layout.conf(dir);
if (null != conf && conf.canRead() && conf.isFile()) {
return true;
}
// try source path
File src = layout.source(dir);
if (null != src && src.canRead() && src.isDirectory()) {
return true;
}
return false;
}
/**
* Build project layout from properties file. The file content shall match the
* project layout interface structure, e.g:
* <pre>
* srccode=src/main/java
* lib=lib
* routes=src/main/resources/routes
* conf=src/main/resources/conf
* target=tmp
* </pre>
*
* @param p
* @return a ProjectLayout instance
*/
public static ProjectLayout build(Properties p) {
String source = _get("source", p);
String testSource = _get("testSource", p);
String lib = _get("lib", p);
String testLib = _get("testLib", p);
String resource = _get("resource", p);
String testResource = _get("testResource", p);
String asset = _get("asset", p);
String routes = _get("routes", p);
String conf = _get("conf", p);
String target = _get("target", p);
return new CustomizedProjectLayout(source, testSource, resource, testResource, lib, testLib, asset, target, routes, conf);
}
private static String _get(String key, Properties p) {
String s = p.getProperty(key);
E.invalidConfigurationIf(null == s, "cannot findBy '%s' setting in project layout properties", key);
return s;
}
}
class CustomizedProjectLayout implements ProjectLayout {
private String source;
private String testSource;
private String lib;
private String testLib;
private String routeTable;
private String conf;
private String target;
private String asset;
private String resource;
private String testResource;
public CustomizedProjectLayout(String src, String testSource, String resource, String testResource, String lib, String testLib, String asset, String tgt, String routeTable, String conf) {
this.source = src;
this.testSource = testSource;
this.resource = resource;
this.testResource = testResource;
this.lib = lib;
this.testLib = testLib;
this.asset = asset;
this.target = tgt;
this.routeTable = routeTable;
this.conf = conf;
}
@Override
public File source(File appBase) {
return file(appBase, source);
}
@Override
public File testSource(File appBase) {
return file(appBase, testSource);
}
@Override
public File lib(File appBase) {
return file(appBase, lib);
}
@Override
public File testLib(File appBase) {
return file(appBase, testLib);
}
@Override
public List<File> routeTables(File appBase) {
List<File> files = new ArrayList<>();
files.add(file(appBase, routeTable));
File confRoot = file(appBase, conf);
files.add(file(confRoot, routeTable));
File profileRoot = file(confRoot, Act.profile());
files.add(file(profileRoot, routeTable));
return files;
}
@Override
public File resource(File appBase) {
return file(appBase, resource);
}
@Override
public File testResource(File appBase) {
return file(appBase, testResource);
}
@Override
public File asset(File appBase) {
return file(appBase, asset);
}
@Override
public File conf(File appBase) {
return file(appBase, conf);
}
@Override
public File target(File appBase) {
return file(appBase, target);
}
}
enum Utils {
;
public static File file(File parent, String path) {
try {
return new File(parent, path).getCanonicalFile();
} catch (IOException e) {
throw E.ioException(e);
}
}
}
public enum F {
;
public static $.F2<File, ProjectLayout, File> SRC = new $.F2<File, ProjectLayout, File>() {
@Override
public File apply(File base, ProjectLayout layout) throws NotAppliedException, Osgl.Break {
return layout.source(base);
}
};
public static $.F2<File, ProjectLayout, File> RSRC = new $.F2<File, ProjectLayout, File>() {
@Override
public File apply(File base, ProjectLayout layout) throws NotAppliedException, Osgl.Break {
return layout.resource(base);
}
};
public static $.F2<File, ProjectLayout, File> LIB = new $.F2<File, ProjectLayout, File>() {
@Override
public File apply(File base, ProjectLayout layout) throws NotAppliedException, Osgl.Break {
return layout.lib(base);
}
};
public static $.F2<File, ProjectLayout, File> TST_SRC = new $.F2<File, ProjectLayout, File>() {
@Override
public File apply(File base, ProjectLayout layout) throws NotAppliedException, Osgl.Break {
return layout.testSource(base);
}
};
public static $.F2<File, ProjectLayout, File> TST_RSRC = new $.F2<File, ProjectLayout, File>() {
@Override
public File apply(File base, ProjectLayout layout) throws NotAppliedException, Osgl.Break {
return layout.testResource(base);
}
};
public static $.F2<File, ProjectLayout, File> TST_LIB = new $.F2<File, ProjectLayout, File>() {
@Override
public File apply(File base, ProjectLayout layout) throws NotAppliedException, Osgl.Break {
return layout.testLib(base);
}
};
}
}