Content of file DefaultSite.java.html
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../../jacoco-resources/report.gif" type="image/gif"/><title>DefaultSite.java</title><link rel="stylesheet" href="../../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../../index.html" class="el_report">NorthernWind :: Filesystems :: SCM</a> > <a href="../index.html" class="el_bundle">it-tidalwave-northernwind-core-default</a> > <a href="index.source.html" class="el_package">it.tidalwave.northernwind.core.impl.model</a> > <span class="el_source">DefaultSite.java</span></div><h1>DefaultSite.java</h1><pre class="source lang-java linenums">/*
* #%L
* *********************************************************************************************************************
*
* NorthernWind - lightweight CMS
* http://northernwind.tidalwave.it - git clone https://bitbucket.org/tidalwave/northernwind-src.git
* %%
* Copyright (C) 2011 - 2023 Tidalwave s.a.s. (http://tidalwave.it)
* %%
* *********************************************************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* *********************************************************************************************************************
*
*
* *********************************************************************************************************************
* #L%
*/
package it.tidalwave.northernwind.core.impl.model;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Configurable;
import it.tidalwave.util.NotFoundException;
import it.tidalwave.northernwind.core.model.Content;
import it.tidalwave.northernwind.core.model.Media;
import it.tidalwave.northernwind.core.model.ModelFactory;
import it.tidalwave.northernwind.core.model.Resource;
import it.tidalwave.northernwind.core.model.ResourceFile;
import it.tidalwave.northernwind.core.model.ResourceFileSystem;
import it.tidalwave.northernwind.core.model.ResourceFileSystemProvider;
import it.tidalwave.northernwind.core.model.ResourcePath;
import it.tidalwave.northernwind.core.model.Site;
import it.tidalwave.northernwind.core.model.SiteFinder;
import it.tidalwave.northernwind.core.model.SiteNode;
import it.tidalwave.northernwind.core.model.Template;
import it.tidalwave.northernwind.core.model.spi.LinkPostProcessor;
import it.tidalwave.northernwind.core.model.spi.RequestHolder;
import it.tidalwave.northernwind.core.impl.text.St4TemplateFactory;
import it.tidalwave.northernwind.core.impl.util.RegexTreeMap;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
/***********************************************************************************************************************
*
* The default implementation of {@link Site}.
*
* @author Fabrizio Giudici
*
**********************************************************************************************************************/
<span class="fc" id="L71">@Configurable @Slf4j</span>
/* package */ class DefaultSite implements InternalSite
{
@Inject
private List<LinkPostProcessor> linkPostProcessors;
@Inject
private RequestHolder requestHolder;
@Nonnull
private final ModelFactory modelFactory;
<span class="nc" id="L83"> @Inject @Named("fileSystemProvider") @Getter</span>
private ResourceFileSystemProvider fileSystemProvider;
@Nonnull
/* package */ final String documentPath;
@Nonnull
/* package */ final String mediaPath;
@Nonnull
/* package */ final String libraryPath;
@Nonnull
/* package */ final String nodePath;
<span class="nc" id="L98"> @Getter</span>
/* package */ final boolean logConfigurationEnabled;
<span class="fc" id="L101"> @Getter @Nonnull</span>
/* package */ final String contextPath;
<span class="fc" id="L104"> /* package */ final List<String> ignoredFolders = new ArrayList<>();</span>
private ResourceFile documentFolder;
private ResourceFile libraryFolder;
private ResourceFile mediaFolder;
<span class="nc" id="L112"> @Getter</span>
private ResourceFile nodeFolder;
// Note that this class can't be final neither use ImmutableMaps, since during the traversing of the filesystem
// resources need to access the partially created internal structure.
<span class="fc" id="L118"> /* package */ final Map<String, Content> documentMapByRelativePath = new TreeMap<>();</span>
<span class="fc" id="L120"> /* package */ final Map<String, Resource> libraryMapByRelativePath = new TreeMap<>();</span>
<span class="fc" id="L122"> /* package */ final Map<String, Media> mediaMapByRelativePath = new TreeMap<>();</span>
<span class="fc" id="L124"> /* package */ final Map<String, SiteNode> nodeMapByRelativePath = new TreeMap<>();</span>
<span class="fc" id="L126"> /* package */ final RegexTreeMap<SiteNode> nodeMapByRelativeUri = new RegexTreeMap<>();</span>
<span class="fc" id="L128"> private final Map<Class<?>, Map<String, ?>> relativePathMapsByType = new HashMap<>();</span>
<span class="fc" id="L130"> private final Map<Class<?>, RegexTreeMap<?>> relativeUriMapsByType = new HashMap<>();</span>
<span class="fc" id="L132"> private final List<Locale> configuredLocales = new ArrayList<>();</span>
<span class="fc" id="L134"> private final Predicate<ResourceFile> FOLDER_FILTER =</span>
<span class="pc bpc" id="L135" title="1 of 4 branches missed."> file -> file.isFolder() && !ignoredFolders.contains(file.getName());</span>
<span class="fc" id="L137"> private final Predicate<ResourceFile> FILE_FILTER =</span>
<span class="fc bfc" id="L138" title="All 4 branches covered."> file -> file.isData() && !ignoredFolders.contains(file.getName());</span>
/*******************************************************************************************************************
*
*
*
******************************************************************************************************************/
protected DefaultSite (@Nonnull final Site.Builder siteBuilder)
<span class="pc bpc" id="L146" title="9 of 18 branches missed."> {</span>
<span class="fc" id="L147"> this.modelFactory = siteBuilder.getModelFactory();</span>
<span class="fc" id="L148"> this.contextPath = siteBuilder.getContextPath();</span>
<span class="fc" id="L149"> this.documentPath = siteBuilder.getDocumentPath();</span>
<span class="fc" id="L150"> this.mediaPath = siteBuilder.getMediaPath();</span>
<span class="fc" id="L151"> this.libraryPath = siteBuilder.getLibraryPath();</span>
<span class="fc" id="L152"> this.nodePath = siteBuilder.getNodePath();</span>
<span class="fc" id="L153"> this.logConfigurationEnabled = siteBuilder.isLogConfigurationEnabled();</span>
<span class="fc" id="L154"> this.configuredLocales.addAll(siteBuilder.getConfiguredLocales());</span>
<span class="fc" id="L155"> this.ignoredFolders.addAll(siteBuilder.getIgnoredFolders());</span>
<span class="pc bpc" id="L156" title="2 of 4 branches missed."> }</span>
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull @SuppressWarnings("unchecked")
public <Type> SiteFinder<Type> find (@Nonnull final Class<Type> type)
{
<span class="fc" id="L166"> final var relativePathMap = (Map<String, Type>)relativePathMapsByType.get(type);</span>
<span class="fc" id="L167"> final var relativeUriMap = (RegexTreeMap<Type>)relativeUriMapsByType.get(type);</span>
<span class="fc" id="L168"> return new DefaultSiteFinder<>(type.getSimpleName(), relativePathMap, relativeUriMap);</span>
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public String createLink (@Nonnull final ResourcePath relativeUri)
{
<span class="fc" id="L179"> final var link = ResourcePath.of(contextPath).appendedWith(relativeUri);</span>
<span class="fc" id="L180"> var linkAsString = requestHolder.get().getBaseUrl() + link.asString();</span>
<span class="fc bfc" id="L182" title="All 2 branches covered."> for (final var linkPostProcessor : linkPostProcessors)</span>
{
<span class="fc" id="L184"> linkAsString = linkPostProcessor.postProcess(linkAsString);</span>
<span class="fc" id="L185"> }</span>
<span class="fc" id="L187"> return linkAsString;</span>
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public List<Locale> getConfiguredLocales()
{
<span class="fc" id="L198"> return new CopyOnWriteArrayList<>(configuredLocales);</span>
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public Template getTemplate (@Nonnull final Class<?> clazz,
@Nonnull final Optional<? extends ResourcePath> templatePath,
@Nonnull final String embeddedResourceName)
{
<span class="nc" id="L211"> return getTemplateFactoryFor(clazz).getTemplate(templatePath, embeddedResourceName);</span>
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public Optional<String> getTemplate (@Nonnull final Class<?> clazz, @Nonnull final ResourcePath templatePath)
{
<span class="nc" id="L222"> return getTemplateFactoryFor(clazz).getTemplate(templatePath);</span>
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
******************************************************************************************************************/
@Override @Nonnull
public String toString()
{
<span class="nc" id="L233"> return String.format("DefaultSite(@%x)", System.identityHashCode(this));</span>
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
/* package */ void initialize()
throws IOException, NotFoundException
{
<span class="fc" id="L243"> log.info("initialize()");</span>
<span class="fc" id="L245"> relativePathMapsByType.put(Content.class, documentMapByRelativePath);</span>
<span class="fc" id="L246"> relativePathMapsByType.put(Media.class, mediaMapByRelativePath);</span>
<span class="fc" id="L247"> relativePathMapsByType.put(Resource.class, libraryMapByRelativePath);</span>
<span class="fc" id="L248"> relativePathMapsByType.put(SiteNode.class, nodeMapByRelativePath);</span>
<span class="fc" id="L250"> relativeUriMapsByType.put(SiteNode.class, nodeMapByRelativeUri);</span>
<span class="fc" id="L252"> log.info(">>>> fileSystemProvider: {}", fileSystemProvider);</span>
<span class="fc" id="L253"> final var fileSystem = fileSystemProvider.getFileSystem();</span>
<span class="fc" id="L254"> documentFolder = findMandatoryFolder(fileSystem, documentPath);</span>
<span class="fc" id="L255"> libraryFolder = findMandatoryFolder(fileSystem, libraryPath);</span>
<span class="fc" id="L256"> mediaFolder = findMandatoryFolder(fileSystem, mediaPath);</span>
<span class="fc" id="L257"> nodeFolder = findMandatoryFolder(fileSystem, nodePath);</span>
<span class="fc" id="L259"> log.info(">>>> contextPath: {}", contextPath);</span>
<span class="fc" id="L260"> log.info(">>>> ignoredFolders: {}", ignoredFolders);</span>
<span class="fc" id="L261"> log.info(">>>> fileSystem: {}", fileSystem);</span>
<span class="fc" id="L262"> log.info(">>>> documentPath: {}", documentFolder.getPath().asString());</span>
<span class="fc" id="L263"> log.info(">>>> libraryPath: {}", libraryFolder.getPath().asString());</span>
<span class="fc" id="L264"> log.info(">>>> mediaPath: {}", mediaFolder.getPath().asString());</span>
<span class="fc" id="L265"> log.info(">>>> nodePath: {}", nodeFolder.getPath().asString());</span>
<span class="fc" id="L266"> log.info(">>>> locales: {}", configuredLocales);</span>
<span class="fc" id="L268"> documentMapByRelativePath.clear();</span>
<span class="fc" id="L269"> libraryMapByRelativePath.clear();</span>
<span class="fc" id="L270"> mediaMapByRelativePath.clear();</span>
<span class="fc" id="L271"> nodeMapByRelativePath.clear();</span>
<span class="fc" id="L272"> nodeMapByRelativeUri.clear();</span>
<span class="fc" id="L274"> traverse(libraryFolder, FILE_FILTER, this::createResource);</span>
<span class="fc" id="L275"> traverse(mediaFolder, FILE_FILTER, this::createMedia);</span>
<span class="fc" id="L276"> traverse(documentFolder, FOLDER_FILTER, this::createContent);</span>
<span class="fc" id="L277"> traverse(nodeFolder, FOLDER_FILTER, this::createSiteNode);</span>
<span class="pc bpc" id="L279" title="1 of 2 branches missed."> if (logConfigurationEnabled)</span>
{
<span class="fc" id="L281"> logConfiguration("Documents by relative path:", documentMapByRelativePath);</span>
<span class="fc" id="L282"> logConfiguration("Library by relative path:", libraryMapByRelativePath);</span>
<span class="fc" id="L283"> logConfiguration("Media by relative path:", mediaMapByRelativePath);</span>
<span class="fc" id="L284"> logConfiguration("Nodes by relative path:", nodeMapByRelativePath);</span>
<span class="fc" id="L285"> logConfiguration("Nodes by relative URI:", nodeMapByRelativeUri);</span>
}
<span class="fc" id="L287"> }</span>
/*******************************************************************************************************************
*
******************************************************************************************************************/
private void createContent (@Nonnull final ResourceFile folder, @Nonnull final ResourcePath relativePath)
{
<span class="fc" id="L294"> documentMapByRelativePath.put(relativePath.asString(), modelFactory.createContent().withFolder(folder).build());</span>
<span class="fc" id="L295"> }</span>
/*******************************************************************************************************************
*
******************************************************************************************************************/
private void createMedia (@Nonnull final ResourceFile file, @Nonnull final ResourcePath relativePath)
{
<span class="fc" id="L302"> mediaMapByRelativePath.put(relativePath.asString(), modelFactory.createMedia().withFile(file).build());</span>
<span class="fc" id="L303"> }</span>
/*******************************************************************************************************************
*
******************************************************************************************************************/
private void createResource (@Nonnull final ResourceFile file, @Nonnull final ResourcePath relativePath)
{
<span class="fc" id="L310"> libraryMapByRelativePath.put(relativePath.asString(), modelFactory.createResource().withFile(file).build());</span>
<span class="fc" id="L311"> }</span>
/*******************************************************************************************************************
*
******************************************************************************************************************/
private void createSiteNode (@Nonnull final ResourceFile folder, @Nonnull final ResourcePath relativePath)
{
<span class="fc" id="L318"> final var siteNode = modelFactory.createSiteNode(this, folder);</span>
<span class="fc" id="L319"> nodeMapByRelativePath.put(relativePath.asString(), siteNode);</span>
<span class="pc bpc" id="L321" title="1 of 2 branches missed."> if (!siteNode.isPlaceHolder())</span>
{
<span class="fc" id="L323"> final var relativeUri = siteNode.getRelativeUri();</span>
// Nodes which manage path params are registered with a relativeUri having a wildcard suffix
<span class="fc bfc" id="L325" title="All 2 branches covered."> if (siteNode.getProperty(SiteNode.P_MANAGES_PATH_PARAMS).orElse(false))</span>
{
<span class="pc bpc" id="L327" title="1 of 2 branches missed."> final var suffix = relativeUri.asString().endsWith("/") ? "(|.*$)" : "(|/.*$)";</span>
<span class="fc" id="L328"> nodeMapByRelativeUri.putRegex("^" + RegexTreeMap.escape(relativeUri.asString()) + suffix, siteNode);</span>
<span class="fc" id="L329"> }</span>
else
{
<span class="fc" id="L332"> nodeMapByRelativeUri.put(relativeUri.asString(), siteNode);</span>
}
}
<span class="fc" id="L335"> }</span>
/*******************************************************************************************************************
*
* Traverse the file system with a consumer.
*
* @param folder the folder to traverse
* @param fileFilter the filter for directory contents
* @param consumer the consumer
*
******************************************************************************************************************/
private void traverse (@Nonnull final ResourceFile folder,
@Nonnull final Predicate<? super ResourceFile> fileFilter,
@Nonnull final BiConsumer<ResourceFile, ? super ResourcePath> consumer)
{
<span class="fc" id="L350"> traverse(folder.getPath(), folder, fileFilter, consumer);</span>
<span class="fc" id="L351"> }</span>
/*******************************************************************************************************************
*
* Traverse the file system with a {@link Predicate}.
*
* @param file the file to traverse
* @param fileFilter the filter for directory contents
* @param consumer the consumer
*
******************************************************************************************************************/
private static void traverse (@Nonnull final ResourcePath rootPath,
@Nonnull final ResourceFile file,
@Nonnull final Predicate<? super ResourceFile> fileFilter,
@Nonnull final BiConsumer<? super ResourceFile, ? super ResourcePath> consumer)
{
<span class="fc" id="L367"> log.trace("traverse({}, {}, {}, {})", rootPath, file, fileFilter, consumer);</span>
<span class="fc" id="L368"> final var relativePath = file.getPath().urlDecoded().relativeTo(rootPath);</span>
<span class="fc bfc" id="L370" title="All 2 branches covered."> if (fileFilter.test(file))</span>
{
<span class="fc" id="L372"> consumer.accept(file, relativePath);</span>
}
<span class="fc" id="L375"> file.findChildren().results().forEach(child -> traverse(rootPath, child, fileFilter, consumer));</span>
<span class="fc" id="L376"> }</span>
/*******************************************************************************************************************
*
* Logs the configuration contained in the given map of properties.
*
******************************************************************************************************************/
private static void logConfiguration (@Nonnull final String name, final Map<String, ?> propertyMap)
{
<span class="fc" id="L385"> log.info(name);</span>
<span class="fc" id="L386"> propertyMap.forEach((key, value) -> log.info(">>>> {}: {}", key, value));</span>
<span class="fc" id="L387"> }</span>
/*******************************************************************************************************************
*
* FIXME Wrapper against ResourceFileSystem: its methods should throw NFE by themselves
*
******************************************************************************************************************/
@Nonnull
private static ResourceFile findMandatoryFolder (@Nonnull final ResourceFileSystem fileSystem,
@Nonnull final String path)
throws NotFoundException
{
<span class="fc" id="L399"> return NotFoundException.throwWhenNull(fileSystem.findFileByPath(path), "Cannot find folder: " + path);</span>
// don't log fileSystem.getRoot() since if fileSystem is broken it can trigger secondary errors
// FileUtil.toFile(fileSystem.getRoot()).getAbsolutePath() + "/" + path);
}
/*******************************************************************************************************************
*
******************************************************************************************************************/
@Nonnull
private St4TemplateFactory getTemplateFactoryFor (@Nonnull final Class<?> clazz)
{
// TODO: implement a cache
<span class="nc" id="L411"> return new St4TemplateFactory(clazz, this);</span>
}
}
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.9.202303310957</span></div></body></html>