getAllTags() {
return result;
}
- private void createDocType(final OSchema schema, final String doctype) {
- logger.debug("Create document class '{}'", doctype);
+ private void createDocType(final OSchema schema, final String docType) {
+ logger.debug("Create document class '{}'", docType);
- OClass page = schema.createClass(doctype);
+ OClass page = schema.createClass(docType);
page.createProperty(String.valueOf(DocumentAttributes.SHA1), OType.STRING).setNotNull(true);
- page.createIndex(doctype + "sha1Index", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.SHA1.toString());
+ page.createIndex(docType + "sha1Index", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.SHA1.toString());
page.createProperty(String.valueOf(DocumentAttributes.SOURCE_URI), OType.STRING).setNotNull(true);
- page.createIndex(doctype + "sourceUriIndex", OClass.INDEX_TYPE.UNIQUE, DocumentAttributes.SOURCE_URI.toString());
+ page.createIndex(docType + "sourceUriIndex", OClass.INDEX_TYPE.UNIQUE, DocumentAttributes.SOURCE_URI.toString());
page.createProperty(String.valueOf(DocumentAttributes.CACHED), OType.BOOLEAN).setNotNull(true);
- page.createIndex(doctype + "cachedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.CACHED.toString());
+ page.createIndex(docType + "cachedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.CACHED.toString());
page.createProperty(String.valueOf(DocumentAttributes.RENDERED), OType.BOOLEAN).setNotNull(true);
- page.createIndex(doctype + "renderedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.RENDERED.toString());
+ page.createIndex(docType + "renderedIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.RENDERED.toString());
page.createProperty(String.valueOf(DocumentAttributes.STATUS), OType.STRING).setNotNull(true);
- page.createIndex(doctype + "statusIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.STATUS.toString());
+ page.createIndex(docType + "statusIndex", OClass.INDEX_TYPE.NOTUNIQUE, DocumentAttributes.STATUS.toString());
}
private void createSignatureType(OSchema schema) {
@@ -295,4 +340,56 @@ private void createSignatureType(OSchema schema) {
signatures.createProperty(String.valueOf(DocumentAttributes.SHA1), OType.STRING).setNotNull(true);
signatures.createIndex("sha1Idx", OClass.INDEX_TYPE.UNIQUE, DocumentAttributes.SHA1.toString());
}
+
+ public void updateAndClearCacheIfNeeded(boolean needed, File templateFolder) {
+
+ boolean clearCache = needed;
+
+ if (!needed) {
+ clearCache = updateTemplateSignatureIfChanged(templateFolder);
+ }
+
+ if (clearCache) {
+ deleteAllDocumentTypes();
+ this.updateSchema();
+ }
+ }
+
+ private boolean updateTemplateSignatureIfChanged(File templateFolder) {
+ boolean templateSignatureChanged = false;
+
+ DocumentList docs = this.getSignaturesForTemplates();
+ String currentTemplatesSignature;
+ try {
+ currentTemplatesSignature = FileUtil.sha1(templateFolder);
+ } catch (Exception e) {
+ currentTemplatesSignature = "";
+ }
+ if (!docs.isEmpty()) {
+ String sha1 = (String) docs.get(0).get(String.valueOf(DocumentAttributes.SHA1));
+ if (!sha1.equals(currentTemplatesSignature)) {
+ this.updateSignatures(currentTemplatesSignature);
+ templateSignatureChanged = true;
+ }
+ } else {
+ // first computation of templates signature
+ this.insertTemplatesSignature(currentTemplatesSignature);
+ templateSignatureChanged = true;
+ }
+ return templateSignatureChanged;
+ }
+
+ private void deleteAllDocumentTypes() {
+ for (String docType : DocumentTypes.getDocumentTypes()) {
+ try {
+ this.deleteAllByDocType(docType);
+ } catch (Exception e) {
+ // maybe a non existing document type
+ }
+ }
+ }
+
+ public boolean isActive() {
+ return db.isActiveOnCurrentThread();
+ }
}
diff --git a/jbake-core/src/main/java/org/jbake/app/Crawler.java b/jbake-core/src/main/java/org/jbake/app/Crawler.java
index d80a326ea..3904d7248 100644
--- a/jbake-core/src/main/java/org/jbake/app/Crawler.java
+++ b/jbake-core/src/main/java/org/jbake/app/Crawler.java
@@ -1,14 +1,11 @@
package org.jbake.app;
-import java.io.File;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Map;
-
+import com.orientechnologies.orient.core.record.impl.ODocument;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.io.FilenameUtils;
-import org.jbake.app.ConfigUtil.Keys;
import org.jbake.app.Crawler.Attributes.Status;
+import org.jbake.app.configuration.JBakeConfiguration;
+import org.jbake.app.configuration.JBakeConfigurationFactory;
import org.jbake.model.DocumentAttributes;
import org.jbake.model.DocumentStatus;
import org.jbake.model.DocumentTypes;
@@ -16,7 +13,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.orientechnologies.orient.core.record.impl.ODocument;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Map;
/**
* Crawls a file system looking for content.
@@ -24,54 +24,51 @@
* @author Jonathan Bullock jonbullock@gmail.com
*/
public class Crawler {
- public interface Attributes {
- /**
- * Possible values of the {@link Attributes#STATUS} property
- *
- * @author ndx
- */
- interface Status {
- String PUBLISHED_DATE = "published-date";
- String PUBLISHED = "published";
- String DRAFT = "draft";
- }
-
- String DATE = "date";
- String STATUS = "status";
- String TYPE = "type";
- String TITLE = "title";
- String URI = "uri";
- String FILE = "file";
- String TAGS = "tags";
- String TAG = "tag";
- String ROOTPATH = "rootpath";
- String ID = "id";
- String NO_EXTENSION_URI = "noExtensionUri";
- String ALLTAGS = "alltags";
- String PUBLISHED_DATE = "published_date";
- String BODY = "body";
- String DB = "db";
- }
private static final Logger LOGGER = LoggerFactory.getLogger(Crawler.class);
-
- private CompositeConfiguration config;
- private Parser parser;
+ public static final String URI_SEPARATOR_CHAR = "/";
private final ContentStore db;
- private String contentPath;
+ private JBakeConfiguration config;
+ private Parser parser;
/**
- * Creates new instance of Crawler.
- *
* @param db Database instance for content
* @param source Base directory where content directory is located
* @param config Project configuration
+ * @deprecated Use {@link #Crawler(ContentStore, JBakeConfiguration)} instead.
+ *
+ * Creates new instance of Crawler.
*/
+ @Deprecated
public Crawler(ContentStore db, File source, CompositeConfiguration config) {
+ this.db = db;
+ this.config = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(source, config);
+ this.parser = new Parser(this.config);
+ }
+
+ /**
+ * Creates new instance of Crawler.
+ *
+ * @param db Database instance for content
+ * @param config Project configuration
+ */
+ public Crawler(ContentStore db, JBakeConfiguration config) {
this.db = db;
this.config = config;
- this.contentPath = FilenameUtils.concat(source.getAbsolutePath(), config.getString(ConfigUtil.Keys.CONTENT_FOLDER));
- this.parser = new Parser(config, contentPath);
+ this.parser = new Parser(config);
+ }
+
+ public void crawl() {
+ crawl(config.getContentFolder());
+
+ LOGGER.info("Content detected:");
+ for (String docType : DocumentTypes.getDocumentTypes()) {
+ long count = db.getDocumentCount(docType);
+ if (count > 0) {
+ LOGGER.info("Parsed {} files of type: {}", count, docType);
+ }
+ }
+
}
/**
@@ -79,7 +76,7 @@ public Crawler(ContentStore db, File source, CompositeConfiguration config) {
*
* @param path Folder to start from
*/
- public void crawl(File path) {
+ private void crawl(File path) {
File[] contents = path.listFiles(FileUtil.getFileFilter());
if (contents != null) {
Arrays.sort(contents);
@@ -93,14 +90,13 @@ public void crawl(File path) {
DocumentStatus status = DocumentStatus.NEW;
for (String docType : DocumentTypes.getDocumentTypes()) {
status = findDocumentStatus(docType, uri, sha1);
- switch (status) {
- case UPDATED:
- sb.append(" : modified ");
- db.deleteContent(docType, uri);
- break;
- case IDENTICAL:
- sb.append(" : same ");
- process = false;
+ if (status == DocumentStatus.UPDATED) {
+ sb.append(" : modified ");
+ db.deleteContent(docType, uri);
+
+ } else if (status == DocumentStatus.IDENTICAL) {
+ sb.append(" : same ");
+ process = false;
}
if (!process) {
break;
@@ -126,17 +122,19 @@ private String buildHash(final File sourceFile) {
try {
sha1 = FileUtil.sha1(sourceFile);
} catch (Exception e) {
- e.printStackTrace();
+ LOGGER.error("unable to build sha1 hash for source file '{}'", sourceFile);
sha1 = "";
}
return sha1;
}
private String buildURI(final File sourceFile) {
- String uri = FileUtil.asPath(sourceFile.getPath())
- .replace(FileUtil.asPath(contentPath), "")
- // On windows we have to replace the backslash
- .replace(File.separator, "/");
+ String uri = FileUtil.asPath(sourceFile).replace(FileUtil.asPath(config.getContentFolder()), "");
+
+ // On windows we have to replace the backslash
+ if (!File.separator.equals(URI_SEPARATOR_CHAR)) {
+ uri = uri.replace(File.separator, URI_SEPARATOR_CHAR);
+ }
if (useNoExtensionUri(uri)) {
// convert URI from xxx.html to xxx/index.html
@@ -146,27 +144,29 @@ private String buildURI(final File sourceFile) {
}
// strip off leading / to enable generating non-root based sites
- if (uri.startsWith("/")) {
+ if (uri.startsWith(URI_SEPARATOR_CHAR)) {
uri = uri.substring(1, uri.length());
}
+
return uri;
}
private String createUri(String uri) {
- return uri.substring(0, uri.lastIndexOf('.')) + config.getString(Keys.OUTPUT_EXTENSION);
+ return uri.substring(0, uri.lastIndexOf('.')) + config.getOutputExtension();
}
private String createNoExtensionUri(String uri) {
- return "/"
+ return URI_SEPARATOR_CHAR
+ FilenameUtils.getPath(uri)
+ FilenameUtils.getBaseName(uri)
- + "/index"
- + config.getString(Keys.OUTPUT_EXTENSION);
+ + URI_SEPARATOR_CHAR
+ + "index"
+ + config.getOutputExtension();
}
private boolean useNoExtensionUri(String uri) {
- boolean noExtensionUri = config.getBoolean(Keys.URI_NO_EXTENSION);
- String noExtensionUriPrefix = config.getString(Keys.URI_NO_EXTENSION_PREFIX);
+ boolean noExtensionUri = config.getUriWithoutExtension();
+ String noExtensionUriPrefix = config.getPrefixForUriWithoutExtension();
return noExtensionUri
&& (noExtensionUriPrefix != null)
@@ -198,13 +198,13 @@ private void crawlSourceFile(final File sourceFile, final String sha1, final Str
}
}
- if (config.getBoolean(Keys.URI_NO_EXTENSION)) {
+ if (config.getUriWithoutExtension()) {
fileContents.put(Attributes.NO_EXTENSION_URI, uri.replace("/index.html", "/"));
}
-
-
+
+
// Prevent image source url's from breaking
- HtmlUtil.fixImageSourceUrls(fileContents,config);
+ HtmlUtil.fixImageSourceUrls(fileContents, config);
ODocument doc = new ODocument(documentType);
doc.fromMap(fileContents);
@@ -216,22 +216,22 @@ private void crawlSourceFile(final File sourceFile, final String sha1, final Str
}
}
- public String getPathToRoot(File sourceFile) {
- File rootPath = new File(contentPath);
- File parentPath = sourceFile.getParentFile();
- int parentCount = 0;
- while (!parentPath.equals(rootPath)) {
- parentPath = parentPath.getParentFile();
- parentCount++;
- }
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < parentCount; i++) {
- sb.append("../");
- }
- if (config.getBoolean(Keys.URI_NO_EXTENSION)) {
- sb.append("../");
+ private String getPathToRoot(File sourceFile) {
+ File rootPath = config.getContentFolder();
+ File parentPath = sourceFile.getParentFile();
+ int parentCount = 0;
+ while (!parentPath.equals(rootPath)) {
+ parentPath = parentPath.getParentFile();
+ parentCount++;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < parentCount; i++) {
+ sb.append("../");
+ }
+ if (config.getUriWithoutExtension()) {
+ sb.append("../");
}
- return sb.toString();
+ return sb.toString();
}
private DocumentStatus findDocumentStatus(String docType, String uri, String sha1) {
@@ -248,4 +248,41 @@ private DocumentStatus findDocumentStatus(String docType, String uri, String sha
return DocumentStatus.NEW;
}
}
+
+ public abstract static class Attributes {
+
+ public static final String DATE = "date";
+ public static final String STATUS = "status";
+ public static final String TYPE = "type";
+ public static final String TITLE = "title";
+ public static final String URI = "uri";
+ public static final String FILE = "file";
+ public static final String TAGS = "tags";
+ public static final String TAG = "tag";
+ public static final String ROOTPATH = "rootpath";
+ public static final String ID = "id";
+ public static final String NO_EXTENSION_URI = "noExtensionUri";
+ public static final String ALLTAGS = "alltags";
+ public static final String PUBLISHED_DATE = "published_date";
+ public static final String BODY = "body";
+ public static final String DB = "db";
+
+ /**
+ * Possible values of the {@link Attributes#STATUS} property
+ *
+ * @author ndx
+ */
+ public abstract static class Status {
+ public static final String PUBLISHED_DATE = "published-date";
+ public static final String PUBLISHED = "published";
+ public static final String DRAFT = "draft";
+
+ private Status() {
+ }
+ }
+
+ private Attributes() {
+ }
+
+ }
}
diff --git a/jbake-core/src/main/java/org/jbake/app/DBUtil.java b/jbake-core/src/main/java/org/jbake/app/DBUtil.java
index 0446bcc96..efb0593bf 100644
--- a/jbake-core/src/main/java/org/jbake/app/DBUtil.java
+++ b/jbake-core/src/main/java/org/jbake/app/DBUtil.java
@@ -2,6 +2,7 @@
import com.orientechnologies.orient.core.db.record.OTrackedList;
import com.orientechnologies.orient.core.record.impl.ODocument;
+import org.jbake.app.configuration.JBakeConfiguration;
import java.util.HashMap;
import java.util.Iterator;
@@ -9,24 +10,34 @@
public class DBUtil {
private static ContentStore contentStore;
-
+
+ @Deprecated
public static ContentStore createDataStore(final String type, String name) {
if (contentStore == null) {
contentStore = new ContentStore(type, name);
}
return contentStore;
}
-
- public static void closeDataStore() {
- contentStore = null;
- }
-
+
+ @Deprecated
public static void updateSchema(final ContentStore db) {
db.updateSchema();
}
+ public static ContentStore createDataStore(JBakeConfiguration configuration) {
+ if (contentStore == null) {
+ contentStore = new ContentStore(configuration.getDatabaseStore(), configuration.getDatabasePath());
+ }
+
+ return contentStore;
+ }
+
+ public static void closeDataStore() {
+ contentStore = null;
+ }
+
public static Map documentToModel(ODocument doc) {
- Map result = new HashMap();
+ Map result = new HashMap<>();
Iterator> fieldIterator = doc.iterator();
while (fieldIterator.hasNext()) {
Map.Entry entry = fieldIterator.next();
diff --git a/jbake-core/src/main/java/org/jbake/app/FileUtil.java b/jbake-core/src/main/java/org/jbake/app/FileUtil.java
index 495eb45ee..416c83ecb 100644
--- a/jbake-core/src/main/java/org/jbake/app/FileUtil.java
+++ b/jbake-core/src/main/java/org/jbake/app/FileUtil.java
@@ -1,7 +1,5 @@
package org.jbake.app;
-import org.apache.commons.configuration.CompositeConfiguration;
-import org.jbake.app.ConfigUtil.Keys;
import org.jbake.parser.Engines;
import java.io.File;
@@ -31,16 +29,16 @@ public static FileFilter getFileFilter() {
@Override
public boolean accept(File pathname) {
//Accept if input is a non-hidden file with registered extension
- //or if a non-hidden and not-ignored directory
+ //or if a non-hidden and not-ignored directory
return !pathname.isHidden() && (pathname.isFile()
&& Engines.getRecognizedExtensions().contains(fileExt(pathname))) || (directoryOnlyIfNotIgnored(pathname));
}
};
}
-
+
/**
* Gets the list of files that are not content files based on their extension.
- *
+ *
* @return FileFilter object
*/
public static FileFilter getNotContentFileFilter() {
@@ -49,15 +47,15 @@ public static FileFilter getNotContentFileFilter() {
@Override
public boolean accept(File pathname) {
//Accept if input is a non-hidden file with NOT-registered extension
- //or if a non-hidden and not-ignored directory
+ //or if a non-hidden and not-ignored directory
return !pathname.isHidden() && (pathname.isFile()
//extension should not be from registered content extensions
- && !Engines.getRecognizedExtensions().contains(fileExt(pathname)))
+ && !Engines.getRecognizedExtensions().contains(fileExt(pathname)))
|| (directoryOnlyIfNotIgnored(pathname));
}
};
}
-
+
/**
* Ignores directory (and children) if it contains a file named ".jbakeignore".
* @param file {@link File}
@@ -65,16 +63,16 @@ public boolean accept(File pathname) {
*/
public static boolean directoryOnlyIfNotIgnored(File file){
boolean accept = false;
-
+
FilenameFilter ignoreFile = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.equalsIgnoreCase(".jbakeignore");
}
- };
-
+ };
+
accept = file.isDirectory() && (file.listFiles(ignoreFile).length == 0);
-
+
return accept;
}
@@ -157,15 +155,6 @@ private static void updateDigest(final MessageDigest digest, final File sourceFi
}
}
- public static String findExtension(CompositeConfiguration config, String docType) {
- String extension = config.getString("template."+docType+".extension");
- if (extension != null) {
- return extension;
- } else {
- return config.getString(Keys.OUTPUT_EXTENSION);
- }
- }
-
/**
* platform independent file.getPath()
*
diff --git a/jbake-core/src/main/java/org/jbake/app/Oven.java b/jbake-core/src/main/java/org/jbake/app/Oven.java
index c62de212d..9eb9a6781 100644
--- a/jbake-core/src/main/java/org/jbake/app/Oven.java
+++ b/jbake-core/src/main/java/org/jbake/app/Oven.java
@@ -1,10 +1,10 @@
package org.jbake.app;
import org.apache.commons.configuration.CompositeConfiguration;
-import org.apache.commons.configuration.ConfigurationException;
-import org.apache.commons.io.FilenameUtils;
-import org.jbake.app.ConfigUtil.Keys;
-import org.jbake.model.DocumentAttributes;
+import org.jbake.app.configuration.DefaultJBakeConfiguration;
+import org.jbake.app.configuration.JBakeConfiguration;
+import org.jbake.app.configuration.JBakeConfigurationFactory;
+import org.jbake.app.configuration.JBakeConfigurationInspector;
import org.jbake.model.DocumentTypes;
import org.jbake.render.RenderingTool;
import org.jbake.template.ModelExtractors;
@@ -14,9 +14,11 @@
import org.slf4j.LoggerFactory;
import java.io.File;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ServiceLoader;
/**
* All the baking happens in the Oven!
@@ -25,158 +27,133 @@
*/
public class Oven {
- private final static Logger LOGGER = LoggerFactory.getLogger(Oven.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(Oven.class);
- private final static Pattern TEMPLATE_DOC_PATTERN = Pattern.compile("(?:template\\.)([a-zA-Z0-9-_]+)(?:\\.file)");
-
- private CompositeConfiguration config;
- private File source;
- private File destination;
- private File templatesPath;
- private File contentsPath;
- private File assetsPath;
- private boolean isClearCache;
- private List errors = new LinkedList();
- private int renderedCount = 0;
+ private Utensils utensils;
+ private List errors = new LinkedList<>();
+ private int renderedCount = 0;
/**
+ * @param source Project source directory
+ * @param destination The destination folder
+ * @param isClearCache Should the cache be cleaned
+ * @throws Exception if configuration is not loaded correctly
+ * @deprecated Use {@link #Oven(JBakeConfiguration)} instead
* Delegate c'tor to prevent API break for the moment.
- *
- * @param source Project source directory
- * @param destination The destination folder
- * @param isClearCache Should the cache be cleaned
- * @throws ConfigurationException if configuration is not loaded correctly
*/
- public Oven(final File source, final File destination, final boolean isClearCache) throws ConfigurationException {
- this(source, destination, ConfigUtil.load(source), isClearCache);
+ @Deprecated
+ public Oven(final File source, final File destination, final boolean isClearCache) throws Exception {
+ this(new JBakeConfigurationFactory().createDefaultJbakeConfiguration(source, destination, isClearCache));
}
/**
+ * @param source Project source directory
+ * @param destination The destination folder
+ * @param config Project configuration
+ * @param isClearCache Should the cache be cleaned
+ * @throws Exception if configuration is not loaded correctly
+ * @deprecated Use {@link #Oven(JBakeConfiguration)} instead
* Creates a new instance of the Oven with references to the source and destination folders.
- *
- * @param source Project source directory
- * @param destination The destination folder
- * @param config Project configuration
- * @param isClearCache Should the cache be cleaned
*/
- public Oven(final File source, final File destination, final CompositeConfiguration config, final boolean isClearCache) {
- this.source = source;
- this.destination = destination;
- this.config = config;
- this.isClearCache = isClearCache;
+ @Deprecated
+ public Oven(final File source, final File destination, final CompositeConfiguration config, final boolean isClearCache) throws Exception {
+ this(new JBakeConfigurationFactory().createDefaultJbakeConfiguration(source, destination, config, isClearCache));
}
- public CompositeConfiguration getConfig() {
- return config;
+ /**
+ * Create an Oven instance by a {@link JBakeConfiguration}
+ *
+ * It creates default {@link Utensils} needed to bake sites.
+ *
+ * @param config The project configuration. see {@link JBakeConfiguration}
+ */
+ public Oven(JBakeConfiguration config) {
+ this.utensils = UtensilsFactory.createDefaultUtensils(config);
}
- // TODO: do we want to use this. Else, config could be final
- public void setConfig(final CompositeConfiguration config) {
- this.config = config;
+ /**
+ * Create an Oven instance with given {@link Utensils}
+ *
+ * @param utensils All Utensils necessary to bake
+ */
+ public Oven(Utensils utensils) {
+ checkConfiguration(utensils.getConfiguration());
+ this.utensils = utensils;
}
- private void ensureSource() {
- if (!FileUtil.isExistingFolder(source)) {
- throw new JBakeException("Error: Source folder must exist: " + source.getAbsolutePath());
- }
- if (!source.canRead()) {
- throw new JBakeException("Error: Source folder is not readable: " + source.getAbsolutePath());
- }
+ @Deprecated
+ public CompositeConfiguration getConfig() {
+ return ((DefaultJBakeConfiguration) utensils.getConfiguration()).getCompositeConfiguration();
}
- private void ensureDestination() {
- if (null == destination) {
- destination = new File(source, config.getString(Keys.DESTINATION_FOLDER));
- }
- if (!destination.exists()) {
- destination.mkdirs();
- }
- if (!destination.canWrite()) {
- throw new JBakeException("Error: Destination folder is not writable: " + destination.getAbsolutePath());
- }
+ // TODO: do we want to use this. Else, config could be final
+ @Deprecated
+ public void setConfig(final CompositeConfiguration config) {
+ ((DefaultJBakeConfiguration) utensils.getConfiguration()).setCompositeConfiguration(config);
}
- private File setupPathFromConfig(String key) {
- return new File(FilenameUtils.concat(source.getAbsolutePath(), config.getString(key)));
+ /**
+ * Checks source path contains required sub-folders (i.e. templates) and setups up variables for them.
+ *
+ * @deprecated There is no need for this method anymore. Validation is now part of the instantiation.
+ * Can be removed with 3.0.0.
+ */
+ @Deprecated
+ public void setupPaths() {
+ /* nothing to do here */
}
/**
* Checks source path contains required sub-folders (i.e. templates) and setups up variables for them.
+ * Creates destination folder if it does not exist.
*
* @throws JBakeException If template or contents folder don't exist
*/
- public void setupPaths() {
- ensureSource();
- templatesPath = setupRequiredFolderFromConfig(Keys.TEMPLATE_FOLDER);
- contentsPath = setupRequiredFolderFromConfig(Keys.CONTENT_FOLDER);
- assetsPath = setupPathFromConfig(Keys.ASSET_FOLDER);
- if (!assetsPath.exists()) {
- LOGGER.warn("No asset folder was found!");
- }
- ensureDestination();
- }
-
- private File setupRequiredFolderFromConfig(final String key) {
- final File path = setupPathFromConfig(key);
- if (!FileUtil.isExistingFolder(path)) {
- throw new JBakeException("Error: Required folder cannot be found! Expected to find [" + key + "] at: " + path.getAbsolutePath());
- }
- return path;
- }
-
- /**
- * All the good stuff happens in here...
- *
- */
- public void bake() {
- final ContentStore db = DBUtil.createDataStore(config.getString(Keys.DB_STORE), config.getString(Keys.DB_PATH));
+ private void checkConfiguration(JBakeConfiguration configuration) {
+ JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(configuration);
+ inspector.inspect();
+ }
+
+ /**
+ * All the good stuff happens in here...
+ */
+ public void bake() {
+
+ ContentStore contentStore = utensils.getContentStore();
+ JBakeConfiguration config = utensils.getConfiguration();
+ Crawler crawler = utensils.getCrawler();
+ Asset asset = utensils.getAsset();
+
+ try {
+
+ final long start = new Date().getTime();
+ LOGGER.info("Baking has started...");
+ contentStore.startup();
updateDocTypesFromConfiguration();
- DBUtil.updateSchema(db);
- try {
- final long start = new Date().getTime();
- LOGGER.info("Baking has started...");
- clearCacheIfNeeded(db);
-
- // process source content
- Crawler crawler = new Crawler(db, source, config);
- crawler.crawl(contentsPath);
- LOGGER.info("Content detected:");
- for (String docType : DocumentTypes.getDocumentTypes()) {
- long count = db.getDocumentCount(docType);
- if (count > 0) {
- LOGGER.info("Parsed {} files of type: {}", count, docType);
- }
- }
-
- Renderer renderer = new Renderer(db, destination, templatesPath, config);
-
- for(RenderingTool tool : ServiceLoader.load(RenderingTool.class)) {
- try {
- renderedCount += tool.render(renderer, db, destination, templatesPath, config);
- } catch(RenderingException e) {
- errors.add(e);
- }
- }
-
- // mark docs as rendered
- for (String docType : DocumentTypes.getDocumentTypes()) {
- db.markContentAsRendered(docType);
- }
- // copy assets
- Asset asset = new Asset(source, destination, config);
- asset.copy(assetsPath);
- asset.copyAssetsFromContent(contentsPath);
- errors.addAll(asset.getErrors());
-
- LOGGER.info("Baking finished!");
- long end = new Date().getTime();
- LOGGER.info("Baked {} items in {}ms", renderedCount, end - start);
- if (errors.size() > 0) {
- LOGGER.error("Failed to bake {} item(s)!", errors.size());
- }
+ contentStore.updateSchema();
+ contentStore.updateAndClearCacheIfNeeded(config.getClearCache(), config.getTemplateFolder());
+
+ // process source content
+ crawler.crawl();
+
+ // render content
+ renderContent();
+
+ // copy assets
+ asset.copy();
+ asset.copyAssetsFromContent(config.getContentFolder());
+
+ errors.addAll(asset.getErrors());
+
+ LOGGER.info("Baking finished!");
+ long end = new Date().getTime();
+ LOGGER.info("Baked {} items in {}ms", renderedCount, end - start);
+ if (!errors.isEmpty()) {
+ LOGGER.error("Failed to bake {} item(s)!", errors.size());
+ }
} finally {
- db.close();
- db.shutdown();
+ contentStore.close();
+ contentStore.shutdown();
}
}
@@ -186,16 +163,13 @@ public void bake() {
*/
private void updateDocTypesFromConfiguration() {
resetDocumentTypesAndExtractors();
+ JBakeConfiguration config = utensils.getConfiguration();
+
ModelExtractorsDocumentTypeListener listener = new ModelExtractorsDocumentTypeListener();
DocumentTypes.addListener(listener);
- Iterator keyIterator = config.getKeys();
- while (keyIterator.hasNext()) {
- String key = keyIterator.next();
- Matcher matcher = TEMPLATE_DOC_PATTERN.matcher(key);
- if (matcher.find()) {
- DocumentTypes.addDocumentType(matcher.group(1));
- }
+ for (String docType : config.getDocumentTypes()) {
+ DocumentTypes.addDocumentType(docType);
}
}
@@ -204,42 +178,29 @@ private void resetDocumentTypesAndExtractors() {
ModelExtractors.getInstance().reset();
}
- private void clearCacheIfNeeded(final ContentStore db) {
- boolean needed = isClearCache;
- if (!needed) {
- DocumentList docs = db.getSignaturesForTemplates();
- String currentTemplatesSignature;
+ /**
+ * Load {@link RenderingTool} instances and delegate rendering of documents to them
+ */
+ private void renderContent() {
+ JBakeConfiguration config = utensils.getConfiguration();
+ Renderer renderer = utensils.getRenderer();
+ ContentStore contentStore = utensils.getContentStore();
+
+ for (RenderingTool tool : ServiceLoader.load(RenderingTool.class)) {
try {
- currentTemplatesSignature = FileUtil.sha1(templatesPath);
- } catch (Exception e) {
- currentTemplatesSignature = "";
+ renderedCount += tool.render(renderer, contentStore, config);
+ } catch (RenderingException e) {
+ errors.add(e);
}
- if (!docs.isEmpty()) {
- String sha1 = (String) docs.get(0).get(String.valueOf(DocumentAttributes.SHA1));
- needed = !sha1.equals(currentTemplatesSignature);
- if (needed) {
- db.updateSignatures(currentTemplatesSignature);
- }
- } else {
- // first computation of templates signature
- db.insertSignature(currentTemplatesSignature);
- needed = true;
- }
- }
- if (needed) {
- for (String docType : DocumentTypes.getDocumentTypes()) {
- try {
- db.deleteAllByDocType(docType);
- } catch (Exception e) {
- // maybe a non existing document type
- }
- }
- DBUtil.updateSchema(db);
}
}
- public List getErrors() {
- return new ArrayList(errors);
- }
+ public List getErrors() {
+ return new ArrayList<>(errors);
+ }
+
+ public Utensils getUtensils() {
+ return utensils;
+ }
}
diff --git a/jbake-core/src/main/java/org/jbake/app/Parser.java b/jbake-core/src/main/java/org/jbake/app/Parser.java
index d7570c678..21d884cba 100644
--- a/jbake-core/src/main/java/org/jbake/app/Parser.java
+++ b/jbake-core/src/main/java/org/jbake/app/Parser.java
@@ -1,6 +1,6 @@
package org.jbake.app;
-import org.apache.commons.configuration.Configuration;
+import org.jbake.app.configuration.JBakeConfiguration;
import org.jbake.parser.Engines;
import org.jbake.parser.ParserEngine;
import org.slf4j.Logger;
@@ -17,25 +17,22 @@
public class Parser {
private static final Logger LOGGER = LoggerFactory.getLogger(Parser.class);
- private Configuration config;
- private String contentPath;
+ private JBakeConfiguration config;
/**
* Creates a new instance of Parser.
*
- * @param config Project configuration
- * @param contentPath Content location
+ * @param config Project configuration
*/
- public Parser(Configuration config, String contentPath) {
+ public Parser(JBakeConfiguration config) {
this.config = config;
- this.contentPath = contentPath;
}
/**
* Process the file by parsing the contents.
*
* @param file File input for parsing
- * @return The contents of the file as a @{@link Map}
+ * @return The contents of the file
*/
public Map processFile(File file) {
ParserEngine engine = Engines.get(FileUtil.fileExt(file));
@@ -44,6 +41,6 @@ public Map processFile(File file) {
return null;
}
- return engine.parse(config, file, contentPath);
+ return engine.parse(config, file);
}
}
diff --git a/jbake-core/src/main/java/org/jbake/app/Renderer.java b/jbake-core/src/main/java/org/jbake/app/Renderer.java
index 6cdf802a2..e39c3c144 100644
--- a/jbake-core/src/main/java/org/jbake/app/Renderer.java
+++ b/jbake-core/src/main/java/org/jbake/app/Renderer.java
@@ -1,14 +1,20 @@
package org.jbake.app;
import org.apache.commons.configuration.CompositeConfiguration;
-import org.jbake.app.ConfigUtil.Keys;
import org.jbake.app.Crawler.Attributes;
+import org.jbake.app.configuration.DefaultJBakeConfiguration;
+import org.jbake.app.configuration.JBakeConfiguration;
+import org.jbake.app.configuration.JBakeConfigurationFactory;
import org.jbake.template.DelegatingTemplateEngine;
import org.jbake.util.PagingHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.*;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -20,189 +26,110 @@
* @author Jonathan Bullock jonbullock@gmail.com
*/
public class Renderer {
-
private static final String MASTERINDEX_TEMPLATE_NAME = "masterindex";
private static final String SITEMAP_TEMPLATE_NAME = "sitemap";
private static final String FEED_TEMPLATE_NAME = "feed";
private static final String ARCHIVE_TEMPLATE_NAME = "archive";
- private interface RenderingConfig {
-
- File getPath();
-
- String getName();
-
- String getTemplate();
-
- Map getModel();
- }
-
- private static abstract class AbstractRenderingConfig implements RenderingConfig {
-
- protected final File path;
- protected final String name;
- protected final String template;
-
- public AbstractRenderingConfig(File path, String name, String template) {
- super();
- this.path = path;
- this.name = name;
- this.template = template;
- }
-
- @Override
- public File getPath() {
- return path;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String getTemplate() {
- return template;
- }
-
- }
-
- public class ModelRenderingConfig extends AbstractRenderingConfig {
- private final Map model;
-
- public ModelRenderingConfig(String fileName, Map model, String templateType) {
- super(new File(destination.getPath() + File.separator + fileName), fileName, findTemplateName(templateType));
- this.model = model;
- }
-
- public ModelRenderingConfig(File path, String name, Map model, String template) {
- super(path, name, template);
- this.model = model;
- }
-
- @Override
- public Map getModel() {
- return model;
- }
- }
-
- class DefaultRenderingConfig extends AbstractRenderingConfig {
-
- private final Object content;
-
- private DefaultRenderingConfig(File path, String allInOneName) {
- super(path, allInOneName, findTemplateName(allInOneName));
- this.content = buildSimpleModel(allInOneName);
- }
-
- public DefaultRenderingConfig(String filename, String allInOneName) {
- super(new File(destination.getPath() + File.separator + filename), allInOneName, findTemplateName(allInOneName));
- this.content = buildSimpleModel(allInOneName);
- }
-
- /**
- * Constructor added due to known use of a allInOneName which is used for name, template and content
- *
- * @param allInOneName
- */
- public DefaultRenderingConfig(String allInOneName) {
- this(new File(destination.getPath() + File.separator + allInOneName + config.getString(Keys.OUTPUT_EXTENSION)),
- allInOneName);
- }
-
- @Override
- public Map getModel() {
- Map model = new HashMap();
- model.put("renderer", renderingEngine);
- model.put("content", content);
-
- if ( config.containsKey(Keys.PAGINATE_INDEX) && config.getBoolean(Keys.PAGINATE_INDEX) ) {
- model.put("numberOfPages", 0);
- model.put("currentPageNumber", 0);
- model.put("previousFileName", "");
- model.put("nextFileName", "");
- }
-
- return model;
- }
-
- }
-
- private final static Logger LOGGER = LoggerFactory.getLogger(Renderer.class);
-
- // TODO: should all content be made available to all templates via this class??
-
- private final File destination;
- private final CompositeConfiguration config;
+ private final Logger LOGGER = LoggerFactory.getLogger(Renderer.class);
+ private final JBakeConfiguration config;
private final DelegatingTemplateEngine renderingEngine;
private final ContentStore db;
/**
- * Creates a new instance of Renderer with supplied references to folders.
- *
* @param db The database holding the content
* @param destination The destination folder
* @param templatesPath The templates folder
* @param config Project configuration
+ * @deprecated Use {@link #Renderer(ContentStore, JBakeConfiguration)} instead.
+ * Creates a new instance of Renderer with supplied references to folders.
*/
+ @Deprecated
public Renderer(ContentStore db, File destination, File templatesPath, CompositeConfiguration config) {
- this.destination = destination;
+ this(db, new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(), config));
+ DefaultJBakeConfiguration configuration = ((DefaultJBakeConfiguration) this.config);
+ configuration.setDestinationFolder(destination);
+ configuration.setTemplateFolder(templatesPath);
+ }
+
+ // TOqDO: should all content be made available to all templates via this class??
+
+ /**
+ * @param db The database holding the content
+ * @param destination The destination folder
+ * @param templatesPath The templates folder
+ * @param config Project configuration
+ * @param renderingEngine The instance of DelegatingTemplateEngine to use
+ * @deprecated Use {@link #Renderer(ContentStore, JBakeConfiguration, DelegatingTemplateEngine)} instead.
+ * Creates a new instance of Renderer with supplied references to folders and the instance of DelegatingTemplateEngine to use.
+ */
+ @Deprecated
+ public Renderer(ContentStore db, File destination, File templatesPath, CompositeConfiguration config, DelegatingTemplateEngine renderingEngine) {
+ this(db, new JBakeConfigurationFactory().createDefaultJbakeConfiguration(templatesPath.getParentFile(), config), renderingEngine);
+ DefaultJBakeConfiguration configuration = ((DefaultJBakeConfiguration) this.config);
+ configuration.setDestinationFolder(destination);
+ configuration.setTemplateFolder(templatesPath);
+ }
+
+ /**
+ * Creates a new instance of Renderer with supplied references to folders.
+ *
+ * @param db The database holding the content
+ * @param config Project configuration
+ */
+ public Renderer(ContentStore db, JBakeConfiguration config) {
this.config = config;
- this.renderingEngine = new DelegatingTemplateEngine(config, db, destination, templatesPath);
+ this.renderingEngine = new DelegatingTemplateEngine(db, config);
this.db = db;
}
-
+
/**
* Creates a new instance of Renderer with supplied references to folders and the instance of DelegatingTemplateEngine to use.
*
- * @param db The database holding the content
- * @param destination The destination folder
- * @param templatesPath The templates folder
- * @param config Project configuration
- * @param renderingEngine The instance of DelegatingTemplateEngine to use
+ * @param db The database holding the content
+ * @param config The application specific configuration
+ * @param renderingEngine The instance of DelegatingTemplateEngine to use
*/
- public Renderer(ContentStore db, File destination, File templatesPath, CompositeConfiguration config, DelegatingTemplateEngine renderingEngine) {
- this.destination = destination;
+ public Renderer(ContentStore db, JBakeConfiguration config, DelegatingTemplateEngine renderingEngine) {
this.config = config;
this.renderingEngine = renderingEngine;
this.db = db;
}
private String findTemplateName(String docType) {
- String templateKey = "template." + docType + ".file";
- String returned = config.getString(templateKey);
- return returned;
+ return config.getTemplateFileByDocType(docType).getName();
}
/**
* Render the supplied content to a file.
*
- * @param content The content to renderDocument
- * @throws Exception if IOException or SecurityException are raised
+ * @param content The content to renderDocument
+ * @throws Exception if IOException or SecurityException are raised
*/
public void render(Map content) throws Exception {
String docType = (String) content.get(Crawler.Attributes.TYPE);
- String outputFilename = destination.getPath() + File.separatorChar + ((String)content.get(Attributes.URI)).replace("/",File.separator);
+ String outputFilename = config.getDestinationFolder().getPath() + File.separatorChar + content.get(Attributes.URI);
if (outputFilename.lastIndexOf('.') > outputFilename.lastIndexOf(File.separatorChar)) {
outputFilename = outputFilename.substring(0, outputFilename.lastIndexOf('.'));
}
// delete existing versions if they exist in case status has changed either way
- File draftFile = new File(outputFilename + config.getString(Keys.DRAFT_SUFFIX) + FileUtil.findExtension(config, docType));
+ String outputExtension = config.getOutputExtensionByDocType(docType);
+ File draftFile = new File(outputFilename, config.getDraftSuffix() + outputExtension);
if (draftFile.exists()) {
draftFile.delete();
}
- File publishedFile = new File(outputFilename + FileUtil.findExtension(config, docType));
+ File publishedFile = new File(outputFilename + outputExtension);
if (publishedFile.exists()) {
publishedFile.delete();
}
if (content.get(Crawler.Attributes.STATUS).equals(Crawler.Attributes.Status.DRAFT)) {
- outputFilename = outputFilename + config.getString(Keys.DRAFT_SUFFIX);
+ outputFilename = outputFilename + config.getDraftSuffix();
}
- File outputFile = new File(outputFilename + FileUtil.findExtension(config, docType));
+ File outputFile = new File(outputFilename + outputExtension);
Map model = new HashMap();
model.put("content", content);
model.put("renderer", renderingEngine);
@@ -224,7 +151,7 @@ private Writer createWriter(File file) throws IOException {
file.createNewFile();
}
- return new OutputStreamWriter(new FileOutputStream(file), config.getString(ConfigUtil.Keys.RENDER_ENCODING));
+ return new OutputStreamWriter(new FileOutputStream(file), config.getRenderEncoding());
}
private void render(RenderingConfig renderConfig) throws Exception {
@@ -253,7 +180,7 @@ public void renderIndex(String indexFile) throws Exception {
public void renderIndexPaging(String indexFile) throws Exception {
long totalPosts = db.getPublishedCount("post");
- int postsPerPage = config.getInt(Keys.POSTS_PER_PAGE, 5);
+ int postsPerPage = config.getPostsPerPage();
if (totalPosts == 0) {
//paging makes no sense. render single index file instead
@@ -264,7 +191,7 @@ public void renderIndexPaging(String indexFile) throws Exception {
Map model = new HashMap();
model.put("renderer", renderingEngine);
model.put("numberOfPages", pagingHelper.getNumberOfPages());
-
+
try {
db.setLimit(postsPerPage);
for (int pageStart = 0, page = 1; pageStart < totalPosts; pageStart += postsPerPage, page++) {
@@ -276,11 +203,11 @@ public void renderIndexPaging(String indexFile) throws Exception {
model.put("previousFileName", previous);
String nextFileName = pagingHelper.getNextFileName(page);
model.put("nextFileName", nextFileName);
-
+
Map contentModel = buildSimpleModel(MASTERINDEX_TEMPLATE_NAME);
-
+
if(page > 1){
- contentModel.put(Attributes.ROOTPATH, "../");
+ contentModel.put(Attributes.ROOTPATH, "../");
}
model.put("content", contentModel);
@@ -299,9 +226,8 @@ public void renderIndexPaging(String indexFile) throws Exception {
/**
* Render an XML sitemap file using the supplied content.
*
- * @param sitemapFile configuration for site map
- * @throws Exception if can't create correct default rendering config
- *
+ * @param sitemapFile configuration for site map
+ * @throws Exception if can't create correct default rendering config
* @see About Sitemaps
* @see Sitemap protocol
*/
@@ -312,8 +238,8 @@ public void renderSitemap(String sitemapFile) throws Exception {
/**
* Render an XML feed file using the supplied content.
*
- * @param feedFile The name of the output file
- * @throws Exception if default rendering configuration is not loaded correctly
+ * @param feedFile The name of the output file
+ * @throws Exception if default rendering configuration is not loaded correctly
*/
public void renderFeed(String feedFile) throws Exception {
render(new DefaultRenderingConfig(feedFile, FEED_TEMPLATE_NAME));
@@ -322,8 +248,8 @@ public void renderFeed(String feedFile) throws Exception {
/**
* Render an archive file using the supplied content.
*
- * @param archiveFile The name of the output file
- * @throws Exception if default rendering configuration is not loaded correctly
+ * @param archiveFile The name of the output file
+ * @throws Exception if default rendering configuration is not loaded correctly
*/
public void renderArchive(String archiveFile) throws Exception {
render(new DefaultRenderingConfig(archiveFile, ARCHIVE_TEMPLATE_NAME));
@@ -337,57 +263,57 @@ public void renderArchive(String archiveFile) throws Exception {
* @throws Exception if cannot render tags correctly
*/
public int renderTags(String tagPath) throws Exception {
- int renderedCount = 0;
- final List errors = new LinkedList();
-
- for (String tag : db.getAllTags()) {
- try {
- Map model = new HashMap();
- model.put("renderer", renderingEngine);
- model.put(Attributes.TAG, tag);
- Map map = buildSimpleModel(Attributes.TAG);
- map.put(Attributes.ROOTPATH, "../");
- model.put("content", map);
-
- File path = new File(destination.getPath() + File.separator + tagPath + File.separator + tag + config.getString(Keys.OUTPUT_EXTENSION));
- render(new ModelRenderingConfig(path, Attributes.TAG, model, findTemplateName(Attributes.TAG)));
-
- renderedCount++;
- } catch (Exception e) {
- errors.add(e);
- }
- }
-
- if (config.getBoolean(Keys.RENDER_TAGS_INDEX)) {
- try{
- // Add an index file at root folder of tags.
- // This will prevent directory listing and also provide an option to
- // display all tags page.
- Map model = new HashMap();
- model.put("renderer", renderingEngine);
- Map map = buildSimpleModel(Attributes.TAGS);
- map.put(Attributes.ROOTPATH, "../");
- model.put("content", map);
-
- File path = new File(destination.getPath() + File.separator + tagPath + File.separator + "index" + config.getString(Keys.OUTPUT_EXTENSION));
- render(new ModelRenderingConfig(path, "tagindex", model, findTemplateName("tagsindex")));
- renderedCount++;
- } catch(Exception e){
- errors.add(e);
- }
- }
-
- if (!errors.isEmpty()) {
- StringBuilder sb = new StringBuilder();
- sb.append("Failed to render tags. Cause(s):");
- for (Throwable error : errors) {
- sb.append("\n").append(error.getMessage());
- }
- throw new Exception(sb.toString(), errors.get(0));
- } else {
- return renderedCount;
- }
- }
+ int renderedCount = 0;
+ final List errors = new LinkedList<>();
+
+ for (String tag : db.getAllTags()) {
+ try {
+ Map model = new HashMap<>();
+ model.put("renderer", renderingEngine);
+ model.put(Attributes.TAG, tag);
+ Map map = buildSimpleModel(Attributes.TAG);
+ map.put(Attributes.ROOTPATH, "../");
+ model.put("content", map);
+
+ File path = new File(config.getDestinationFolder() + File.separator + tagPath + File.separator + tag + config.getOutputExtension());
+ render(new ModelRenderingConfig(path, Attributes.TAG, model, findTemplateName(Attributes.TAG)));
+
+ renderedCount++;
+ } catch (Exception e) {
+ errors.add(e);
+ }
+ }
+
+ if (config.getRenderTagsIndex()) {
+ try {
+ // Add an index file at root folder of tags.
+ // This will prevent directory listing and also provide an option to
+ // display all tags page.
+ Map model = new HashMap();
+ model.put("renderer", renderingEngine);
+ Map map = buildSimpleModel(Attributes.TAGS);
+ map.put(Attributes.ROOTPATH, "../");
+ model.put("content", map);
+
+ File path = new File(config.getDestinationFolder() + File.separator + tagPath + File.separator + "index" + config.getOutputExtension());
+ render(new ModelRenderingConfig(path, "tagindex", model, findTemplateName("tagsindex")));
+ renderedCount++;
+ } catch (Exception e) {
+ errors.add(e);
+ }
+ }
+
+ if (!errors.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Failed to render tags. Cause(s):");
+ for (Throwable error : errors) {
+ sb.append("\n").append(error.getMessage());
+ }
+ throw new Exception(sb.toString(), errors.get(0));
+ } else {
+ return renderedCount;
+ }
+ }
/**
* Builds simple map of values, which are exposed when rendering index/archive/sitemap/feed/tags.
@@ -402,4 +328,106 @@ private Map buildSimpleModel(String type) {
// add any more keys here that need to have a default value to prevent need to perform null check in templates
return content;
}
+
+ private interface RenderingConfig {
+
+ File getPath();
+
+ String getName();
+
+ String getTemplate();
+
+ Map getModel();
+ }
+
+ private static abstract class AbstractRenderingConfig implements RenderingConfig {
+
+ protected final File path;
+ protected final String name;
+ protected final String template;
+
+ public AbstractRenderingConfig(File path, String name, String template) {
+ super();
+ this.path = path;
+ this.name = name;
+ this.template = template;
+ }
+
+ @Override
+ public File getPath() {
+ return path;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getTemplate() {
+ return template;
+ }
+
+ }
+
+ public class ModelRenderingConfig extends AbstractRenderingConfig {
+ private final Map model;
+
+ public ModelRenderingConfig(String fileName, Map model, String templateType) {
+ super(new File(config.getDestinationFolder(), fileName), fileName, findTemplateName(templateType));
+ this.model = model;
+ }
+
+ public ModelRenderingConfig(File path, String name, Map model, String template) {
+ super(path, name, template);
+ this.model = model;
+ }
+
+ @Override
+ public Map getModel() {
+ return model;
+ }
+ }
+
+ class DefaultRenderingConfig extends AbstractRenderingConfig {
+
+ private final Object content;
+
+ private DefaultRenderingConfig(File path, String allInOneName) {
+ super(path, allInOneName, findTemplateName(allInOneName));
+ this.content = buildSimpleModel(allInOneName);
+ }
+
+ public DefaultRenderingConfig(String filename, String allInOneName) {
+ super(new File(config.getDestinationFolder(), File.separator + filename), allInOneName, findTemplateName(allInOneName));
+ this.content = buildSimpleModel(allInOneName);
+ }
+
+ /**
+ * Constructor added due to known use of a allInOneName which is used for name, template and content
+ *
+ * @param allInOneName
+ */
+ public DefaultRenderingConfig(String allInOneName) {
+ this(new File(config.getDestinationFolder().getPath() + File.separator + allInOneName + config.getOutputExtension()),
+ allInOneName);
+ }
+
+ @Override
+ public Map getModel() {
+ Map model = new HashMap();
+ model.put("renderer", renderingEngine);
+ model.put("content", content);
+
+ if (config.getPaginateIndex()) {
+ model.put("numberOfPages", 0);
+ model.put("currentPageNumber", 0);
+ model.put("previousFileName", "");
+ model.put("nextFileName", "");
+ }
+
+ return model;
+ }
+
+ }
}
diff --git a/jbake-core/src/main/java/org/jbake/app/Utensils.java b/jbake-core/src/main/java/org/jbake/app/Utensils.java
new file mode 100644
index 000000000..648278cfe
--- /dev/null
+++ b/jbake-core/src/main/java/org/jbake/app/Utensils.java
@@ -0,0 +1,55 @@
+package org.jbake.app;
+
+import org.jbake.app.configuration.JBakeConfiguration;
+
+/**
+ * A helper class to wrap all the utensils that are needed to bake.
+ */
+public class Utensils {
+ private JBakeConfiguration configuration;
+ private ContentStore contentStore;
+ private Crawler crawler;
+ private Renderer renderer;
+ private Asset asset;
+
+ public JBakeConfiguration getConfiguration() {
+ return configuration;
+ }
+
+ public void setConfiguration(JBakeConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ public ContentStore getContentStore() {
+ return contentStore;
+ }
+
+ public void setContentStore(ContentStore contentStore) {
+ this.contentStore = contentStore;
+ }
+
+ public Crawler getCrawler() {
+ return crawler;
+ }
+
+ public void setCrawler(Crawler crawler) {
+ this.crawler = crawler;
+ }
+
+ public Renderer getRenderer() {
+ return renderer;
+ }
+
+ public void setRenderer(Renderer renderer) {
+ this.renderer = renderer;
+ }
+
+ public Asset getAsset() {
+ return asset;
+ }
+
+ public void setAsset(Asset asset) {
+ this.asset = asset;
+ }
+}
+
diff --git a/jbake-core/src/main/java/org/jbake/app/UtensilsFactory.java b/jbake-core/src/main/java/org/jbake/app/UtensilsFactory.java
new file mode 100644
index 000000000..5e2038193
--- /dev/null
+++ b/jbake-core/src/main/java/org/jbake/app/UtensilsFactory.java
@@ -0,0 +1,31 @@
+package org.jbake.app;
+
+import org.jbake.app.configuration.JBakeConfiguration;
+import org.jbake.app.configuration.JBakeConfigurationInspector;
+
+/**
+ * A factory to create a {@link Utensils} object
+ */
+public class UtensilsFactory {
+
+ /**
+ * Create default {@link Utensils} by a given {@link JBakeConfiguration}
+ * @param config a {@link JBakeConfiguration}
+ * @return a default {@link Utensils} instance
+ */
+ public static Utensils createDefaultUtensils(JBakeConfiguration config) {
+
+ JBakeConfigurationInspector inspector = new JBakeConfigurationInspector(config);
+ inspector.inspect();
+
+ Utensils utensils = new Utensils();
+ utensils.setConfiguration(config);
+ ContentStore contentStore = DBUtil.createDataStore(config);
+ utensils.setContentStore(contentStore);
+ utensils.setCrawler(new Crawler(contentStore, config));
+ utensils.setRenderer(new Renderer(contentStore, config));
+ utensils.setAsset(new Asset(config));
+
+ return utensils;
+ }
+}
diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/ConfigUtil.java b/jbake-core/src/main/java/org/jbake/app/configuration/ConfigUtil.java
new file mode 100644
index 000000000..9768d72d2
--- /dev/null
+++ b/jbake-core/src/main/java/org/jbake/app/configuration/ConfigUtil.java
@@ -0,0 +1,59 @@
+package org.jbake.app.configuration;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.commons.configuration.SystemConfiguration;
+import org.jbake.app.JBakeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+/**
+ * Provides Configuration related functions.
+ *
+ * @author Jonathan Bullock jonbullock@gmail.com
+ */
+public class ConfigUtil {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class);
+ private static final String LEGACY_CONFIG_FILE = "custom.properties";
+ private static final String CONFIG_FILE = "jbake.properties";
+ private static final String DEFAULT_CONFIG_FILE = "default.properties";
+
+ private CompositeConfiguration load(File source) throws ConfigurationException {
+
+ if (!source.exists()) {
+ throw new JBakeException("The given source folder '" + source.getAbsolutePath() + "' does not exist.");
+ }
+ if (!source.isDirectory()) {
+ throw new JBakeException("The given source folder is not a directory.");
+ }
+
+ CompositeConfiguration config = new CompositeConfiguration();
+ config.setListDelimiter(',');
+ File customConfigFile = new File(source, LEGACY_CONFIG_FILE);
+ if (customConfigFile.exists()) {
+ displayLegacyConfigFileWarningIfRequired();
+ config.addConfiguration(new PropertiesConfiguration(customConfigFile));
+ }
+ customConfigFile = new File(source, CONFIG_FILE);
+ if (customConfigFile.exists()) {
+ config.addConfiguration(new PropertiesConfiguration(customConfigFile));
+ }
+ config.addConfiguration(new PropertiesConfiguration(DEFAULT_CONFIG_FILE));
+ config.addConfiguration(new SystemConfiguration());
+ return config;
+ }
+
+ private void displayLegacyConfigFileWarningIfRequired() {
+ LOGGER.warn("You have defined a part of your JBake configuration in {}", LEGACY_CONFIG_FILE);
+ LOGGER.warn("Usage of this file is being deprecated, please rename this file to: {} to remove this warning", CONFIG_FILE);
+ }
+
+ public JBakeConfiguration loadConfig(File source) throws ConfigurationException {
+ CompositeConfiguration configuration = load(source);
+ return new DefaultJBakeConfiguration(source, configuration);
+ }
+}
diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/DefaultJBakeConfiguration.java b/jbake-core/src/main/java/org/jbake/app/configuration/DefaultJBakeConfiguration.java
new file mode 100644
index 000000000..804d5052f
--- /dev/null
+++ b/jbake-core/src/main/java/org/jbake/app/configuration/DefaultJBakeConfiguration.java
@@ -0,0 +1,534 @@
+package org.jbake.app.configuration;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The default implementation of a {@link JBakeConfiguration}
+ */
+public class DefaultJBakeConfiguration implements JBakeConfiguration {
+
+
+ private static final String SOURCE_FOLDER_KEY = "sourceFolder";
+ private static final String DESTINATION_FOLDER_KEY = "destinationFolder";
+ private static final String ASSET_FOLDER_KEY = "assetFolder";
+ private static final String TEMPLATE_FOLDER_KEY = "templateFolder";
+ private static final String CONTENT_FOLDER_KEY = "contentFolder";
+ private static final Pattern TEMPLATE_DOC_PATTERN = Pattern.compile("(?:template\\.)([a-zA-Z0-9-_]+)(?:\\.file)");
+ private static final String DOCTYPE_FILE_POSTFIX = ".file";
+ private static final String DOCTYPE_EXTENSION_POSTFIX = ".extension";
+ private static final String DOCTYPE_TEMPLATE_PREFIX = "template.";
+ private Logger logger = LoggerFactory.getLogger(DefaultJBakeConfiguration.class);
+ private CompositeConfiguration compositeConfiguration;
+
+ /**
+ * Some deprecated implementations just need access to the configuration without access to the source folder
+ *
+ * @param configuration The project configuration
+ * @deprecated use {@link #DefaultJBakeConfiguration(File, CompositeConfiguration)} instead
+ */
+ @Deprecated
+ public DefaultJBakeConfiguration(CompositeConfiguration configuration) {
+ this.compositeConfiguration = configuration;
+ }
+
+ public DefaultJBakeConfiguration(File sourceFolder, CompositeConfiguration configuration) {
+ this.compositeConfiguration = configuration;
+ setSourceFolder(sourceFolder);
+ setupDefaultDestination();
+ setupPathsRelativeToSourceFile();
+ }
+
+ @Override
+ public Object get(String key) {
+ return compositeConfiguration.getProperty(key);
+ }
+
+ @Override
+ public String getArchiveFileName() {
+ return getAsString(JBakeProperty.ARCHIVE_FILE);
+ }
+
+ private boolean getAsBoolean(String key) {
+ return compositeConfiguration.getBoolean(key, false);
+ }
+
+ private File getAsFolder(String key) {
+ return (File) get(key);
+ }
+
+ private int getAsInt(String key, int defaultValue) {
+ return compositeConfiguration.getInt(key, defaultValue);
+ }
+
+ private List getAsList(String key) {
+ return Arrays.asList(compositeConfiguration.getStringArray(key));
+ }
+
+ private String getAsString(String key) {
+ return compositeConfiguration.getString(key);
+ }
+
+ private String getAsString(String key, String defaultValue) {
+ return compositeConfiguration.getString(key, defaultValue);
+ }
+
+ @Override
+ public List getAsciidoctorAttributes() {
+ return getAsList(JBakeProperty.ASCIIDOCTOR_ATTRIBUTES);
+ }
+
+ public Object getAsciidoctorOption(String optionKey) {
+ Configuration subConfig = compositeConfiguration.subset(JBakeProperty.ASCIIDOCTOR_OPTION);
+ Object value = subConfig.getProperty(optionKey);
+
+ if (value == null) {
+ logger.warn("Cannot find asciidoctor option '{}.{}'", JBakeProperty.ASCIIDOCTOR_OPTION, optionKey);
+ return "";
+ }
+ return value;
+ }
+
+ @Override
+ public List getAsciidoctorOptionKeys() {
+ List options = new ArrayList<>();
+ Configuration subConfig = compositeConfiguration.subset(JBakeProperty.ASCIIDOCTOR_OPTION);
+
+ Iterator iterator = subConfig.getKeys();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ options.add(key);
+ }
+
+ return options;
+ }
+
+ @Override
+ public File getAssetFolder() {
+ return getAsFolder(ASSET_FOLDER_KEY);
+ }
+
+ public void setAssetFolder(File assetFolder) {
+ if (assetFolder != null) {
+ setProperty(ASSET_FOLDER_KEY, assetFolder);
+ setProperty(JBakeProperty.ASSET_FOLDER, assetFolder.getName());
+ }
+ }
+
+ @Override
+ public String getAssetFolderName() {
+ return getAsString(JBakeProperty.ASSET_FOLDER);
+ }
+
+ @Override
+ public boolean getAssetIgnoreHidden() {
+ return getAsBoolean(JBakeProperty.ASSET_IGNORE_HIDDEN);
+ }
+
+ public void setAssetIgnoreHidden(boolean assetIgnoreHidden) {
+ setProperty(JBakeProperty.ASSET_IGNORE_HIDDEN, assetIgnoreHidden);
+ }
+
+ @Override
+ public String getAttributesExportPrefixForAsciidoctor() {
+ return getAsString(JBakeProperty.ASCIIDOCTOR_ATTRIBUTES_EXPORT_PREFIX, "");
+ }
+
+ @Override
+ public String getBuildTimeStamp() {
+ return getAsString(JBakeProperty.BUILD_TIMESTAMP);
+ }
+
+ @Override
+ public boolean getClearCache() {
+ return getAsBoolean(JBakeProperty.CLEAR_CACHE);
+ }
+
+ public void setClearCache(boolean clearCache) {
+ setProperty(JBakeProperty.CLEAR_CACHE, clearCache);
+ }
+
+ public CompositeConfiguration getCompositeConfiguration() {
+ return compositeConfiguration;
+ }
+
+ public void setCompositeConfiguration(CompositeConfiguration configuration) {
+ this.compositeConfiguration = configuration;
+ }
+
+ @Override
+ public File getContentFolder() {
+ return getAsFolder(CONTENT_FOLDER_KEY);
+ }
+
+ public void setContentFolder(File contentFolder) {
+ if (contentFolder != null) {
+ setProperty(CONTENT_FOLDER_KEY, contentFolder);
+ setProperty(JBakeProperty.CONTENT_FOLDER, contentFolder.getName());
+ }
+ }
+
+ @Override
+ public String getContentFolderName() {
+ return getAsString(JBakeProperty.CONTENT_FOLDER);
+ }
+
+ @Override
+ public String getDatabasePath() {
+ return getAsString(JBakeProperty.DB_PATH);
+ }
+
+ public void setDatabasePath(String path) {
+ setProperty(JBakeProperty.DB_PATH, path);
+ }
+
+ @Override
+ public String getDatabaseStore() {
+ return getAsString(JBakeProperty.DB_STORE);
+ }
+
+ public void setDatabaseStore(String storeType) {
+ setProperty(JBakeProperty.DB_STORE, storeType);
+ }
+
+ @Override
+ public String getDateFormat() {
+ return getAsString(JBakeProperty.DATE_FORMAT);
+ }
+
+ @Override
+ public String getDefaultStatus() {
+ return getAsString(JBakeProperty.DEFAULT_STATUS);
+ }
+
+ public void setDefaultStatus(String status) {
+ setProperty(JBakeProperty.DEFAULT_STATUS, status);
+ }
+
+ @Override
+ public String getDefaultType() {
+ String type = getAsString(JBakeProperty.DEFAULT_TYPE);
+ if (type.isEmpty()) {
+ return null;
+ }
+ return type;
+ }
+
+ public void setDefaultType(String type) {
+ setProperty(JBakeProperty.DEFAULT_TYPE, type);
+ }
+
+ @Override
+ public File getDestinationFolder() {
+ return getAsFolder(DESTINATION_FOLDER_KEY);
+ }
+
+ public void setDestinationFolder(File destinationFolder) {
+ if (destinationFolder != null) {
+ setProperty(DESTINATION_FOLDER_KEY, destinationFolder);
+ setProperty(JBakeProperty.DESTINATION_FOLDER, destinationFolder.getName());
+ }
+ }
+
+ @Override
+ public List getDocumentTypes() {
+ List docTypes = new ArrayList<>();
+ Iterator keyIterator = compositeConfiguration.getKeys();
+ while (keyIterator.hasNext()) {
+ String key = keyIterator.next();
+ Matcher matcher = TEMPLATE_DOC_PATTERN.matcher(key);
+ if (matcher.find()) {
+ docTypes.add(matcher.group(1));
+ }
+ }
+
+ return docTypes;
+ }
+
+ @Override
+ public String getDraftSuffix() {
+ return getAsString(JBakeProperty.DRAFT_SUFFIX, "");
+ }
+
+ @Override
+ public String getExampleProjectByType(String templateType) {
+ return getAsString("example.project." + templateType);
+ }
+
+ @Override
+ public boolean getExportAsciidoctorAttributes() {
+ return getAsBoolean(JBakeProperty.ASCIIDOCTOR_ATTRIBUTES_EXPORT);
+ }
+
+ @Override
+ public String getFeedFileName() {
+ return getAsString(JBakeProperty.FEED_FILE);
+ }
+
+ @Override
+ public String getIndexFileName() {
+ return getAsString(JBakeProperty.INDEX_FILE);
+ }
+
+ @Override
+ public Iterator getKeys() {
+ return compositeConfiguration.getKeys();
+ }
+
+ @Override
+ public List getMarkdownExtensions() {
+ return getAsList(JBakeProperty.MARKDOWN_EXTENSIONS);
+ }
+
+ public void setMarkdownExtensions(String... extensions) {
+ setProperty(JBakeProperty.MARKDOWN_EXTENSIONS, StringUtils.join(extensions, ","));
+ }
+
+ @Override
+ public String getOutputExtension() {
+ return getAsString(JBakeProperty.OUTPUT_EXTENSION);
+ }
+
+ public void setOutputExtension(String outputExtension) {
+ setProperty(JBakeProperty.OUTPUT_EXTENSION, outputExtension);
+ }
+
+ @Override
+ public String getOutputExtensionByDocType(String docType) {
+ String templateExtensionKey = DOCTYPE_TEMPLATE_PREFIX + docType + DOCTYPE_EXTENSION_POSTFIX;
+ String defaultOutputExtension = getOutputExtension();
+ return getAsString(templateExtensionKey, defaultOutputExtension);
+ }
+
+ @Override
+ public boolean getPaginateIndex() {
+ return getAsBoolean(JBakeProperty.PAGINATE_INDEX);
+ }
+
+ public void setPaginateIndex(boolean paginateIndex) {
+ setProperty(JBakeProperty.PAGINATE_INDEX, paginateIndex);
+ }
+
+ @Override
+ public int getPostsPerPage() {
+ return getAsInt(JBakeProperty.POSTS_PER_PAGE, 5);
+ }
+
+ public void setPostsPerPage(int postsPerPage) {
+ setProperty(JBakeProperty.POSTS_PER_PAGE, postsPerPage);
+ }
+
+ @Override
+ public String getPrefixForUriWithoutExtension() {
+ return getAsString(JBakeProperty.URI_NO_EXTENSION_PREFIX);
+ }
+
+ public void setPrefixForUriWithoutExtension(String prefix) {
+ setProperty(JBakeProperty.URI_NO_EXTENSION_PREFIX, prefix);
+ }
+
+ @Override
+ public boolean getRenderArchive() {
+ return getAsBoolean(JBakeProperty.RENDER_ARCHIVE);
+ }
+
+ @Override
+ public String getRenderEncoding() {
+ return getAsString(JBakeProperty.RENDER_ENCODING);
+ }
+
+ @Override
+ public boolean getRenderFeed() {
+ return getAsBoolean(JBakeProperty.RENDER_FEED);
+ }
+
+ @Override
+ public boolean getRenderIndex() {
+ return getAsBoolean(JBakeProperty.RENDER_INDEX);
+ }
+
+ @Override
+ public boolean getRenderSiteMap() {
+ return getAsBoolean(JBakeProperty.RENDER_SITEMAP);
+ }
+
+ @Override
+ public boolean getRenderTags() {
+ return getAsBoolean(JBakeProperty.RENDER_TAGS);
+ }
+
+ @Override
+ public boolean getRenderTagsIndex() {
+ return compositeConfiguration.getBoolean(JBakeProperty.RENDER_TAGS_INDEX, false);
+ }
+
+ public void setRenderTagsIndex(boolean enable) {
+ compositeConfiguration.setProperty(JBakeProperty.RENDER_TAGS_INDEX, enable);
+ }
+
+ @Override
+ public boolean getSanitizeTag() {
+ return getAsBoolean(JBakeProperty.TAG_SANITIZE);
+ }
+
+ @Override
+ public int getServerPort() {
+ return getAsInt(JBakeProperty.SERVER_PORT, 8080);
+ }
+
+ public void setServerPort(int port) {
+ setProperty(JBakeProperty.SERVER_PORT, port);
+ }
+
+ @Override
+ public String getSiteHost() {
+ return getAsString(JBakeProperty.SITE_HOST, "http://www.jbake.org");
+ }
+
+ public void setSiteHost(String siteHost) {
+ setProperty(JBakeProperty.SITE_HOST, siteHost);
+ }
+
+ @Override
+ public String getSiteMapFileName() {
+ return getAsString(JBakeProperty.SITEMAP_FILE);
+ }
+
+ @Override
+ public File getSourceFolder() {
+ return getAsFolder(SOURCE_FOLDER_KEY);
+ }
+
+ public void setSourceFolder(File sourceFolder) {
+ setProperty(SOURCE_FOLDER_KEY, sourceFolder);
+ setupPathsRelativeToSourceFile();
+ }
+
+ @Override
+ public String getTagPathName() {
+ return getAsString(JBakeProperty.TAG_PATH);
+ }
+
+ @Override
+ public String getTemplateEncoding() {
+ return getAsString(JBakeProperty.TEMPLATE_ENCODING);
+ }
+
+ @Override
+ public File getTemplateFileByDocType(String docType) {
+ String templateKey = DOCTYPE_TEMPLATE_PREFIX + docType + DOCTYPE_FILE_POSTFIX;
+ String templateFileName = getAsString(templateKey);
+ if (templateFileName != null) {
+ return new File(getTemplateFolder(), templateFileName);
+ }
+ logger.warn("Cannot find configuration key '{}' for document type '{}'", templateKey, docType);
+ return null;
+ }
+
+ @Override
+ public File getTemplateFolder() {
+ return getAsFolder(TEMPLATE_FOLDER_KEY);
+ }
+
+ public void setTemplateFolder(File templateFolder) {
+ if (templateFolder != null) {
+ setProperty(TEMPLATE_FOLDER_KEY, templateFolder);
+ setProperty(JBakeProperty.TEMPLATE_FOLDER, templateFolder.getName());
+ }
+ }
+
+ @Override
+ public String getTemplateFolderName() {
+ return getAsString(JBakeProperty.TEMPLATE_FOLDER);
+ }
+
+ @Override
+ public String getThymeleafLocale() {
+ return getAsString(JBakeProperty.THYMELEAF_LOCALE);
+ }
+
+ @Override
+ public boolean getUriWithoutExtension() {
+ return getAsBoolean(JBakeProperty.URI_NO_EXTENSION);
+ }
+
+ public void setUriWithoutExtension(boolean withoutExtension) {
+ setProperty(JBakeProperty.URI_NO_EXTENSION, withoutExtension);
+ }
+
+ @Override
+ public String getVersion() {
+ return getAsString(JBakeProperty.VERSION);
+ }
+
+ public void setDestinationFolderName(String folderName) {
+ setProperty(JBakeProperty.DESTINATION_FOLDER, folderName);
+ setupDefaultDestination();
+ }
+
+ public void setExampleProject(String type, String fileName) {
+ String projectKey = "example.project." + type;
+ setProperty(projectKey, fileName);
+ }
+
+ @Override
+ public void setProperty(String key, Object value) {
+ compositeConfiguration.setProperty(key, value);
+ }
+
+ public void setTemplateExtensionForDocType(String docType, String extension) {
+ String templateExtensionKey = DOCTYPE_TEMPLATE_PREFIX + docType + DOCTYPE_EXTENSION_POSTFIX;
+ setProperty(templateExtensionKey, extension);
+ }
+
+ public void setTemplateFileNameForDocType(String docType, String fileName) {
+ String templateKey = DOCTYPE_TEMPLATE_PREFIX + docType + DOCTYPE_FILE_POSTFIX;
+ setProperty(templateKey, fileName);
+ }
+
+ private void setupDefaultAssetFolder() {
+ String assetFolder = getAsString(JBakeProperty.ASSET_FOLDER);
+ setAssetFolder(new File(getSourceFolder(), assetFolder));
+ }
+
+ private void setupDefaultContentFolder() {
+ setContentFolder(new File(getSourceFolder(), getContentFolderName()));
+ }
+
+ private void setupDefaultDestination() {
+ String destinationPath = getAsString(JBakeProperty.DESTINATION_FOLDER);
+ setDestinationFolder(new File(getSourceFolder(), destinationPath));
+ }
+
+ private void setupDefaultTemplateFolder() {
+ String destinationPath = getAsString(JBakeProperty.TEMPLATE_FOLDER);
+ setTemplateFolder(new File(getSourceFolder(), destinationPath));
+ }
+
+ private void setupPathsRelativeToSourceFile() {
+ setupDefaultAssetFolder();
+ setupDefaultTemplateFolder();
+ setupDefaultContentFolder();
+ }
+
+ @Override
+ public String getHeaderSeparator() {
+ return getAsString(JBakeProperty.HEADER_SEPARATOR);
+ }
+
+ public void setHeaderSeparator(String headerSeparator) {
+ setProperty(JBakeProperty.HEADER_SEPARATOR, headerSeparator);
+ }
+
+}
diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfiguration.java b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfiguration.java
new file mode 100644
index 000000000..a44de370f
--- /dev/null
+++ b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfiguration.java
@@ -0,0 +1,298 @@
+package org.jbake.app.configuration;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * JBakeConfiguration gives you access to the project configuration. Typically located in a file called jbake.properties.
+ *
+ * Use one of {@link JBakeConfigurationFactory} methods to create an instance.
+ */
+public interface JBakeConfiguration {
+
+ /**
+ * Get property value by a given key from the configuration
+ *
+ * @param key a key for the property like site.host
+ * @return the value of the property
+ */
+ Object get(String key);
+
+ /**
+ * @return Output filename for archive file, is only used when {@link #getRenderArchive()} is true
+ */
+ String getArchiveFileName();
+
+ /**
+ * @return attributes to be set when processing input
+ */
+ List getAsciidoctorAttributes();
+
+ /**
+ * Get an asciidoctor option by it's key
+ *
+ * @param optionKey an option key
+ * @return the value of the option key
+ */
+ Object getAsciidoctorOption(String optionKey);
+
+ /**
+ * Get a list of asciidoctor options
+ *
+ * @return list of asciidoctor options
+ */
+ List getAsciidoctorOptionKeys();
+
+ /**
+ * @return the folder where assets are stored, they are copied directly in output folder and not processed
+ */
+ File getAssetFolder();
+
+ /**
+ * @return name of folder for assets
+ */
+ String getAssetFolderName();
+
+ /**
+ * @return Flag indicating if hidden asset resources should be ignored
+ */
+ boolean getAssetIgnoreHidden();
+
+ /**
+ * @return Prefix to be used when exporting JBake properties to Asciidoctor
+ */
+ String getAttributesExportPrefixForAsciidoctor();
+
+ /**
+ * @return Timestamp that records when JBake build was made
+ */
+ String getBuildTimeStamp();
+
+ /**
+ * @return Flag indicating to flash the database cache
+ */
+ boolean getClearCache();
+
+ /**
+ * @return the content folder
+ */
+ File getContentFolder();
+
+ /**
+ * @return name of Folder where content (that's to say files to be transformed) resides in
+ */
+ String getContentFolderName();
+
+ /**
+ * @return Folder to store database files in
+ */
+ String getDatabasePath();
+
+ /**
+ * @return name to identify if database is kept in memory (memory) or persisted to disk (plocal)
+ */
+ String getDatabaseStore();
+
+ /**
+ * @return How date is formated
+ */
+ String getDateFormat();
+
+ /**
+ * @return Default status to use (in order to avoid putting it in all files)
+ */
+ String getDefaultStatus();
+
+ /**
+ * @return Default type to use (in order to avoid putting it in all files)
+ */
+ String getDefaultType();
+
+ /**
+ * @return The destination folder to render and copy files to
+ */
+ File getDestinationFolder();
+
+ void setDestinationFolder(File destination);
+
+ List getDocumentTypes();
+
+ /**
+ * @return Suffix used to identify draft files
+ */
+ String getDraftSuffix();
+
+ /**
+ * Get name for example project name by given template type
+ *
+ * @param templateType a template type
+ * @return example project name
+ */
+ String getExampleProjectByType(String templateType);
+
+ /**
+ * @return Flag indicating if JBake properties should be made available to Asciidoctor
+ */
+ boolean getExportAsciidoctorAttributes();
+
+ /**
+ * @return Output filename for feed file, is only used when {@link #getRenderFeed()} der} is true
+ */
+ String getFeedFileName();
+
+
+ /**
+ * @return String used to separate the header from the body
+ */
+ String getHeaderSeparator();
+
+ /**
+ * @return Output filename for index, is only used when {@link #getRenderIndex()} is true
+ */
+ String getIndexFileName();
+
+ /**
+ * Get an iterator of available configuration keys
+ *
+ * @return an iterator of configuration keys
+ */
+ Iterator getKeys();
+
+ /**
+ * A list of markdown extensions
+ *
+ * markdown.extension=HARDWRAPS,AUTOLINKS,FENCED_CODE_BLOCKS,DEFINITIONS
+ *
+ * @return list of markdown extensions as string
+ */
+ List getMarkdownExtensions();
+
+ /**
+ * @return file extension to be used for all output files
+ */
+ String getOutputExtension();
+
+ String getOutputExtensionByDocType(String docType);
+
+ /**
+ * @return Flag indicating if there should be pagination when rendering index
+ */
+ boolean getPaginateIndex();
+
+ /**
+ * @return How many posts per page on index
+ */
+ int getPostsPerPage();
+
+ /**
+ * @return URI prefix for content that should be given extension-less output URI's
+ */
+ String getPrefixForUriWithoutExtension();
+
+ /**
+ * @return Flag indicating if archive file should be generated
+ */
+ boolean getRenderArchive();
+
+ /**
+ * @return Encoding used when rendering files
+ */
+ String getRenderEncoding();
+
+ /**
+ * @return Flag indicating if feed file should be generated
+ */
+ boolean getRenderFeed();
+
+ /**
+ * @return Flag indicating if index file should be generated
+ */
+ boolean getRenderIndex();
+
+ /**
+ * @return Flag indicating if sitemap file should be generated
+ */
+ boolean getRenderSiteMap();
+
+ /**
+ * @return Flag indicating if tag files should be generated
+ */
+ boolean getRenderTags();
+
+ /**
+ * @return Flag indicating if tag index file should be generated
+ */
+ boolean getRenderTagsIndex();
+
+ /**
+ * @return Flag indicating if the tag value should be sanitized
+ */
+ boolean getSanitizeTag();
+
+ /**
+ * @return Port used when running Jetty server
+ */
+ int getServerPort();
+
+ /**
+ * @return the host url of the site e.g. http://jbake.org
+ */
+ String getSiteHost();
+
+ /**
+ * @return Sitemap template file name. Used only when {@link #getRenderSiteMap()} is set to true
+ */
+ String getSiteMapFileName();
+
+ /**
+ * @return the source folder of the project
+ */
+ File getSourceFolder();
+
+ /**
+ * @return Tags output path, used only when {@link #getRenderTags()} is true
+ */
+ String getTagPathName();
+
+ /**
+ * @return Encoding to be used for template files
+ */
+ String getTemplateEncoding();
+
+ File getTemplateFileByDocType(String masterindex);
+
+ /**
+ * @return the template folder
+ */
+ File getTemplateFolder();
+
+ /**
+ * @return name of folder where template files are looked for
+ */
+ String getTemplateFolderName();
+
+ /**
+ * @return Locale used for Thymeleaf template rendering
+ */
+ String getThymeleafLocale();
+
+ /**
+ * @return Flag indicating if content matching prefix below should be given extension-less URI's
+ */
+ boolean getUriWithoutExtension();
+
+ /**
+ * @return Version of JBake
+ */
+ String getVersion();
+
+ /**
+ * Set a property value for the given key
+ *
+ * @param key the key for the property
+ * @param value the value of the property
+ */
+ void setProperty(String key, Object value);
+}
+
diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationFactory.java b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationFactory.java
new file mode 100644
index 000000000..f9bbd4756
--- /dev/null
+++ b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationFactory.java
@@ -0,0 +1,108 @@
+package org.jbake.app.configuration;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.ConfigurationException;
+
+import java.io.File;
+
+/**
+ * A {@link JBakeConfiguration} factory
+ */
+public class JBakeConfigurationFactory {
+
+ private ConfigUtil configUtil;
+
+ public JBakeConfigurationFactory() {
+ this.configUtil = new ConfigUtil();
+ }
+
+ /**
+ * Creates a {@link DefaultJBakeConfiguration}
+ * @param sourceFolder The source folder of the project
+ * @param destination The destination folder to render and copy files to
+ * @param isClearCache Whether to clear database cache or not
+ * @return A configuration by given parameters
+ * @throws ConfigurationException if loading the configuration fails
+ */
+ public DefaultJBakeConfiguration createDefaultJbakeConfiguration(File sourceFolder, File destination, boolean isClearCache) throws ConfigurationException {
+
+ DefaultJBakeConfiguration configuration = (DefaultJBakeConfiguration) getConfigUtil().loadConfig(sourceFolder);
+ configuration.setDestinationFolder(destination);
+ configuration.setClearCache(isClearCache);
+
+ return configuration;
+ }
+
+ /**
+ * Creates a {@link DefaultJBakeConfiguration}
+ *
+ * This is a compatibility factory method
+ *
+ * @param sourceFolder The source folder of the project
+ * @param destination The destination folder to render and copy files to
+ * @param compositeConfiguration A given {@link CompositeConfiguration}
+ * @param isClearCache Whether to clear database cache or not
+ * @return A configuration by given parameters
+ */
+ public DefaultJBakeConfiguration createDefaultJbakeConfiguration(File sourceFolder, File destination, CompositeConfiguration compositeConfiguration, boolean isClearCache) {
+ DefaultJBakeConfiguration configuration = new DefaultJBakeConfiguration(sourceFolder, compositeConfiguration);
+ configuration.setDestinationFolder(destination);
+ configuration.setClearCache(isClearCache);
+ return configuration;
+ }
+
+ /**
+ * Creates a {@link DefaultJBakeConfiguration}
+ *
+ * This is a compatibility factory method
+ *
+ * @param sourceFolder The source folder of the project
+ * @param destination The destination folder to render and copy files to
+ * @param compositeConfiguration A given {@link CompositeConfiguration}
+ * @return A configuration by given parameters
+ */
+ public DefaultJBakeConfiguration createDefaultJbakeConfiguration(File sourceFolder, File destination, CompositeConfiguration compositeConfiguration) {
+ DefaultJBakeConfiguration configuration = new DefaultJBakeConfiguration(sourceFolder, compositeConfiguration);
+ configuration.setDestinationFolder(destination);
+ return configuration;
+ }
+
+ /**
+ * Creates a {@link DefaultJBakeConfiguration}
+ *
+ *
+ * @param sourceFolder The source folder of the project
+ * @param config A {@link CompositeConfiguration}
+ * @return A configuration by given parameters
+ */
+ public DefaultJBakeConfiguration createDefaultJbakeConfiguration(File sourceFolder, CompositeConfiguration config) {
+ return new DefaultJBakeConfiguration(sourceFolder,config);
+ }
+
+ /**
+ * Creates a {@link DefaultJBakeConfiguration} with value site.host replaced
+ * by http://localhost:[server.port].
+ * The server.port is read from the project properties file.
+ *
+ * @param sourceFolder The source folder of the project
+ * @param destinationFolder The destination folder to render and copy files to
+ * @param isClearCache Whether to clear database cache or not
+ * @return A configuration by given parameters
+ * @throws ConfigurationException if loading the configuration fails
+ */
+ public DefaultJBakeConfiguration createJettyJbakeConfiguration(File sourceFolder, File destinationFolder, boolean isClearCache) throws ConfigurationException {
+ DefaultJBakeConfiguration configuration = (DefaultJBakeConfiguration) getConfigUtil().loadConfig(sourceFolder);
+ configuration.setDestinationFolder(destinationFolder);
+ configuration.setClearCache(isClearCache);
+ configuration.setSiteHost("http://localhost:"+configuration.getServerPort());
+ return configuration;
+ }
+
+ public ConfigUtil getConfigUtil() {
+ return configUtil;
+ }
+
+ public void setConfigUtil(ConfigUtil configUtil) {
+ this.configUtil = configUtil;
+ }
+}
diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationInspector.java b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationInspector.java
new file mode 100644
index 000000000..d8c81882f
--- /dev/null
+++ b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeConfigurationInspector.java
@@ -0,0 +1,72 @@
+package org.jbake.app.configuration;
+
+import org.jbake.app.FileUtil;
+import org.jbake.app.JBakeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+public class JBakeConfigurationInspector {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JBakeConfigurationInspector.class);
+
+ private final JBakeConfiguration configuration;
+
+ public JBakeConfigurationInspector(JBakeConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ public void inspect() throws JBakeException {
+ ensureSource();
+ ensureTemplateFolder();
+ ensureContentFolder();
+ ensureDestination();
+ checkAssetFolder();
+ }
+
+ private void ensureSource() throws JBakeException {
+ File source = configuration.getSourceFolder();
+ if (!FileUtil.isExistingFolder(source)) {
+ throw new JBakeException("Error: Source folder must exist: " + source.getAbsolutePath());
+ }
+ if (!configuration.getSourceFolder().canRead()) {
+ throw new JBakeException("Error: Source folder is not readable: " + source.getAbsolutePath());
+ }
+ }
+
+ private void ensureTemplateFolder() {
+ File path = configuration.getTemplateFolder();
+ checkRequiredFolderExists(configuration.getTemplateFolderName(), path);
+ }
+
+ private void ensureContentFolder() {
+ File path = configuration.getContentFolder();
+ checkRequiredFolderExists(configuration.getContentFolderName(), path);
+ }
+
+ private void ensureDestination() {
+ File destination = configuration.getDestinationFolder();
+ if (!destination.exists()) {
+ destination.mkdirs();
+ }
+ if (!destination.canWrite()) {
+ throw new JBakeException("Error: Destination folder is not writable: " + destination.getAbsolutePath());
+ }
+ }
+
+ private void checkAssetFolder() {
+ File path = configuration.getAssetFolder();
+ if (!path.exists()) {
+ LOGGER.warn("No asset folder '{}' was found!", path.getAbsolutePath());
+ }
+ }
+
+ private void checkRequiredFolderExists(String folderName, File path) {
+ if (!FileUtil.isExistingFolder(path)) {
+ throw new JBakeException("Error: Required folder cannot be found! Expected to find [" + folderName + "] at: " + path.getAbsolutePath());
+ }
+ }
+
+
+}
diff --git a/jbake-core/src/main/java/org/jbake/app/configuration/JBakeProperty.java b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeProperty.java
new file mode 100644
index 000000000..052cad701
--- /dev/null
+++ b/jbake-core/src/main/java/org/jbake/app/configuration/JBakeProperty.java
@@ -0,0 +1,50 @@
+package org.jbake.app.configuration;
+
+public class JBakeProperty {
+
+ public static final String ARCHIVE_FILE = "archive.file";
+ public static final String ASCIIDOCTOR_ATTRIBUTES = "asciidoctor.attributes";
+ public static final String ASCIIDOCTOR_ATTRIBUTES_EXPORT = "asciidoctor.attributes.export";
+ public static final String ASCIIDOCTOR_OPTION = "asciidoctor.option";
+ public static final String ASCIIDOCTOR_ATTRIBUTES_EXPORT_PREFIX = "asciidoctor.attributes.export.prefix";
+ public static final String ASSET_FOLDER = "asset.folder";
+ public static final String ASSET_IGNORE_HIDDEN = "asset.ignore";
+ public static final String BUILD_TIMESTAMP = "build.timestamp";
+ public static final String CLEAR_CACHE = "db.clear.cache";
+ public static final String CONTENT_FOLDER = "content.folder";
+ public static final String DATE_FORMAT = "date.format";
+ public static final String DB_STORE = "db.store";
+ public static final String DB_PATH = "db.path";
+ public static final String DEFAULT_STATUS = "default.status";
+ public static final String DEFAULT_TYPE = "default.type";
+ public static final String DESTINATION_FOLDER = "destination.folder";
+ public static final String DRAFT_SUFFIX = "draft.suffix";
+ public static final String FEED_FILE = "feed.file";
+ public static final String HEADER_SEPARATOR = "header.separator";
+ public static final String INDEX_FILE = "index.file";
+ public static final String MARKDOWN_EXTENSIONS = "markdown.extensions";
+ public static final String OUTPUT_EXTENSION = "output.extension";
+ public static final String PAGINATE_INDEX = "index.paginate";
+ public static final String POSTS_PER_PAGE = "index.posts_per_page";
+ public static final String RENDER_ARCHIVE = "render.archive";
+ public static final String RENDER_FEED = "render.feed";
+ public static final String RENDER_INDEX = "render.index";
+ public static final String RENDER_SITEMAP = "render.sitemap";
+ public static final String RENDER_TAGS = "render.tags";
+ public static final String RENDER_TAGS_INDEX = "render.tagsindex";
+ public static final String RENDER_ENCODING = "render.encoding";
+ public static final String SERVER_PORT = "server.port";
+ public static final String SITE_HOST = "site.host";
+ public static final String SITEMAP_FILE = "sitemap.file";
+ public static final String TAG_SANITIZE = "tag.sanitize";
+ public static final String TAG_PATH = "tag.path";
+ public static final String TEMPLATE_FOLDER = "template.folder";
+ public static final String TEMPLATE_ENCODING = "template.encoding";
+ public static final String THYMELEAF_LOCALE = "thymeleaf.locale";
+ public static final String URI_NO_EXTENSION = "uri.noExtension";
+ public static final String URI_NO_EXTENSION_PREFIX = "uri.noExtension.prefix";
+ public static final String VERSION = "version";
+
+ private JBakeProperty() {}
+
+}
diff --git a/jbake-core/src/main/java/org/jbake/launcher/BakeWatcher.java b/jbake-core/src/main/java/org/jbake/launcher/BakeWatcher.java
index a55017321..b6e2df1e8 100644
--- a/jbake-core/src/main/java/org/jbake/launcher/BakeWatcher.java
+++ b/jbake-core/src/main/java/org/jbake/launcher/BakeWatcher.java
@@ -6,7 +6,10 @@
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.VFS;
import org.apache.commons.vfs2.impl.DefaultFileMonitor;
-import org.jbake.app.ConfigUtil;
+import org.jbake.app.configuration.JBakeConfiguration;
+import org.jbake.app.configuration.JBakeConfigurationFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Delegate responsible for watching the file system for changes.
@@ -15,29 +18,43 @@
*/
public class BakeWatcher {
+ private Logger logger = LoggerFactory.getLogger(BakeWatcher.class);
+
/**
* Starts watching the file system for changes to trigger a bake.
*
- * @param res Commandline options
+ * @deprecated use {@link BakeWatcher#start(JBakeConfiguration)} instead
+ *
+ * @param res Commandline options
* @param config Configuration settings
*/
+ @Deprecated
public void start(final LaunchOptions res, CompositeConfiguration config) {
+ JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(res.getSource(), config);
+ start(configuration);
+ }
+
+ /**
+ * Starts watching the file system for changes to trigger a bake.
+ *
+ * @param config JBakeConfiguration settings
+ */
+ public void start(JBakeConfiguration config) {
try {
FileSystemManager fsMan = VFS.getManager();
- FileObject listenPath = fsMan.resolveFile(res.getSource(), config.getString( ConfigUtil.Keys.CONTENT_FOLDER));
- FileObject templateListenPath = fsMan.resolveFile(res.getSource(), config.getString( ConfigUtil.Keys.TEMPLATE_FOLDER));
- FileObject assetPath = fsMan.resolveFile(res.getSource(), config.getString( ConfigUtil.Keys.ASSET_FOLDER));
-
+ FileObject listenPath = fsMan.resolveFile(config.getContentFolder().toURI());
+ FileObject templateListenPath = fsMan.resolveFile(config.getTemplateFolder().toURI());
+ FileObject assetPath = fsMan.resolveFile(config.getAssetFolder().toURI());
- System.out.println("Watching for (content, template, asset) changes in [" + res.getSource() + "]");
- DefaultFileMonitor monitor = new DefaultFileMonitor(new CustomFSChangeListener(res, config));
+ logger.info("Watching for (content, template, asset) changes in [{}]", config.getSourceFolder().getPath());
+ DefaultFileMonitor monitor = new DefaultFileMonitor(new CustomFSChangeListener(config));
monitor.setRecursive(true);
monitor.addFile(listenPath);
monitor.addFile(templateListenPath);
monitor.addFile(assetPath);
monitor.start();
} catch (FileSystemException e) {
- e.printStackTrace();
+ logger.error("Problems watching filesystem changes", e);
}
}
}
diff --git a/jbake-core/src/main/java/org/jbake/launcher/Baker.java b/jbake-core/src/main/java/org/jbake/launcher/Baker.java
index 6641a70bf..a1dcb373e 100644
--- a/jbake-core/src/main/java/org/jbake/launcher/Baker.java
+++ b/jbake-core/src/main/java/org/jbake/launcher/Baker.java
@@ -3,6 +3,8 @@
import org.apache.commons.configuration.CompositeConfiguration;
import org.jbake.app.JBakeException;
import org.jbake.app.Oven;
+import org.jbake.app.configuration.JBakeConfiguration;
+import org.jbake.app.configuration.JBakeConfigurationFactory;
import java.text.MessageFormat;
import java.util.List;
@@ -14,9 +16,19 @@
*/
public class Baker {
+ /**
+ * @param options The given cli options
+ * @param config The project configuration
+ * @deprecated use {@link Baker#bake(JBakeConfiguration)} instead
+ */
+ @Deprecated
public void bake(final LaunchOptions options, final CompositeConfiguration config) {
- final Oven oven = new Oven(options.getSource(), options.getDestination(), config, options.isClearCache());
- oven.setupPaths();
+ JBakeConfiguration configuration = new JBakeConfigurationFactory().createDefaultJbakeConfiguration(options.getSource(), options.getDestination(), config, options.isClearCache());
+ bake(configuration);
+ }
+
+ public void bake(final JBakeConfiguration config) {
+ final Oven oven = new Oven(config);
oven.bake();
final List errors = oven.getErrors();
diff --git a/jbake-core/src/main/java/org/jbake/launcher/CustomFSChangeListener.java b/jbake-core/src/main/java/org/jbake/launcher/CustomFSChangeListener.java
index 9ee1b2099..9e69f544c 100644
--- a/jbake-core/src/main/java/org/jbake/launcher/CustomFSChangeListener.java
+++ b/jbake-core/src/main/java/org/jbake/launcher/CustomFSChangeListener.java
@@ -1,9 +1,9 @@
package org.jbake.launcher;
-import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.vfs2.FileChangeEvent;
import org.apache.commons.vfs2.FileListener;
import org.jbake.app.Oven;
+import org.jbake.app.configuration.JBakeConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -11,11 +11,9 @@ public class CustomFSChangeListener implements FileListener {
private final static Logger LOGGER = LoggerFactory.getLogger(CustomFSChangeListener.class);
- private LaunchOptions options;
- private CompositeConfiguration config;
+ private JBakeConfiguration config;
- public CustomFSChangeListener(LaunchOptions res, CompositeConfiguration config) {
- this.options = res;
+ public CustomFSChangeListener(JBakeConfiguration config) {
this.config = config;
}
@@ -38,8 +36,7 @@ public void fileChanged(FileChangeEvent event) throws Exception {
}
private void exec() {
- final Oven oven = new Oven(options.getSource(), options.getDestination(), config, options.isClearCache());
- oven.setupPaths();
+ final Oven oven = new Oven(config);
oven.bake();
}
diff --git a/jbake-core/src/main/java/org/jbake/launcher/Init.java b/jbake-core/src/main/java/org/jbake/launcher/Init.java
index de0baf92b..51edd839d 100644
--- a/jbake-core/src/main/java/org/jbake/launcher/Init.java
+++ b/jbake-core/src/main/java/org/jbake/launcher/Init.java
@@ -1,8 +1,9 @@
package org.jbake.launcher;
import org.apache.commons.configuration.CompositeConfiguration;
-import org.jbake.app.ConfigUtil.Keys;
import org.jbake.app.ZipUtil;
+import org.jbake.app.configuration.DefaultJBakeConfiguration;
+import org.jbake.app.configuration.JBakeConfiguration;
import java.io.File;
import java.io.FileInputStream;
@@ -15,9 +16,18 @@
*/
public class Init {
- private CompositeConfiguration config;
-
+ private JBakeConfiguration config;
+
+ /**
+ * @param config The project configuration
+ * @deprecated use {@link Init#Init(JBakeConfiguration)} instead
+ */
+ @Deprecated
public Init(CompositeConfiguration config) {
+ this(new DefaultJBakeConfiguration(config));
+ }
+
+ public Init(JBakeConfiguration config) {
this.config = config;
}
@@ -39,13 +49,13 @@ public void run(File outputFolder, File templateLocationFolder, String templateT
if (contents != null) {
for (File content : contents) {
if (content.isDirectory()) {
- if (content.getName().equalsIgnoreCase(config.getString(Keys.TEMPLATE_FOLDER))) {
+ if (content.getName().equalsIgnoreCase(config.getTemplateFolderName())) {
safe = false;
}
- if (content.getName().equalsIgnoreCase(config.getString(Keys.CONTENT_FOLDER))) {
+ if (content.getName().equalsIgnoreCase(config.getContentFolderName())) {
safe = false;
}
- if (content.getName().equalsIgnoreCase(config.getString(Keys.ASSET_FOLDER))) {
+ if (content.getName().equalsIgnoreCase(config.getAssetFolderName())) {
safe = false;
}
}
@@ -56,8 +66,8 @@ public void run(File outputFolder, File templateLocationFolder, String templateT
throw new Exception(String.format("Output folder '%s' already contains structure!",
outputFolder.getAbsolutePath()));
}
- if (config.getString("example.project."+templateType) != null) {
- File templateFile = new File(templateLocationFolder, config.getString("example.project."+templateType));
+ if (config.getExampleProjectByType(templateType) != null) {
+ File templateFile = new File(templateLocationFolder, config.getExampleProjectByType(templateType));
if (!templateFile.exists()) {
throw new Exception("Cannot find example project file: " + templateFile.getPath());
}
diff --git a/jbake-core/src/main/java/org/jbake/launcher/JettyServer.java b/jbake-core/src/main/java/org/jbake/launcher/JettyServer.java
index e82280cce..7e24af0f4 100644
--- a/jbake-core/src/main/java/org/jbake/launcher/JettyServer.java
+++ b/jbake-core/src/main/java/org/jbake/launcher/JettyServer.java
@@ -16,7 +16,7 @@
* @author Jonathan Bullock jonbullock@gmail.com
*/
public class JettyServer {
- private final static Logger LOGGER = LoggerFactory.getLogger(JettyServer.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(JettyServer.class);
/**
* Run Jetty web server serving out supplied path on supplied port
@@ -48,7 +48,7 @@ public void run(String path, String port) {
server.start();
server.join();
} catch (Exception e) {
- e.printStackTrace();
+ LOGGER.error("unable to start server", e);
}
}
}
diff --git a/jbake-core/src/main/java/org/jbake/launcher/LaunchOptions.java b/jbake-core/src/main/java/org/jbake/launcher/LaunchOptions.java
index 4e3ebf2d7..d30bd5865 100644
--- a/jbake-core/src/main/java/org/jbake/launcher/LaunchOptions.java
+++ b/jbake-core/src/main/java/org/jbake/launcher/LaunchOptions.java
@@ -1,83 +1,84 @@
package org.jbake.launcher;
-import java.io.File;
-
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
+import java.io.File;
+
public class LaunchOptions {
- @Argument(index = 0, usage = "source folder of site content (with templates and assets), if not supplied will default to current directory", metaVar = "