hillwig
I'm new here

Probleme mit UrlFactory nach Update auf FirstSpirit 5.2

Hallo zusammen,

wir haben im Rahmen der Einführung von FirstSpirit 5 das mitgelieferte Beispiel zur UrlFactory geringfügig angepasst.

Hier der Quellcode:

import de.espirit.common.StringUtil;

import de.espirit.common.io.IoError;

import de.espirit.firstspirit.access.Language;

import de.espirit.firstspirit.access.project.Resolution;

import de.espirit.firstspirit.access.project.TemplateSet;

import de.espirit.firstspirit.access.store.ContentProducer;

import de.espirit.firstspirit.access.store.IDProvider;

import de.espirit.firstspirit.access.store.LanguageInfo;

import de.espirit.firstspirit.access.store.PageParams;

import de.espirit.firstspirit.access.store.mediastore.File;

import de.espirit.firstspirit.access.store.mediastore.Media;

import de.espirit.firstspirit.access.store.mediastore.MediaMetaData;

import de.espirit.firstspirit.access.store.mediastore.Picture;

import de.espirit.firstspirit.access.store.sitestore.Content2Params;

import de.espirit.firstspirit.access.store.sitestore.ContentMultiPageParams.ContentPageParams;

import de.espirit.firstspirit.access.store.sitestore.PageRef;

import de.espirit.firstspirit.access.store.sitestore.SiteStoreFolder;

import de.espirit.firstspirit.generate.PathLookup;

import de.espirit.firstspirit.generate.UrlFactory;

import de.espirit.or.schema.Entity;

import org.jetbrains.annotations.Nullable;

import java.io.IOException;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

/**

* Search engine-optimized path factory.

*/

public class XUrlFactory implements UrlFactory {

          private PathLookup _pathLookup;

          private boolean _useWelcomeFilenames;

          /**

           * Initialize fields based on various settings and a {@link PathLookup}

           * object.

           *

           * @param settings

           *            Settings provided in module.xml file in section

           *            {@code <configuration>..</configuration>}. The key is the tag

           *            name (converted to lower case), value is the text child node.

           *            E.g. {@code <key>value</key>}.

           * @param pathLookup

           *            Path lookup for user defined paths.

           */

          @Override

          public void init(final Map<String, String> settings, final PathLookup pathLookup) {

                    _pathLookup = pathLookup;

                    final String useWelcomeFilenames = settings.get("usewelcomefilenames");

                    _useWelcomeFilenames = useWelcomeFilenames == null || "yes".equalsIgnoreCase(useWelcomeFilenames)

                                        || "true".equalsIgnoreCase(useWelcomeFilenames);

          }

          /**

           * Build the URL for a content-producing store element.

           *

           * @param contentProducer

           *            A store element.

           * @param templateSet

           *            The target template set.

           * @param language

           *            The target language.

           * @param pageParams

           *            Page parameters, used for content projection, etc.

           * @return The URL for the {@code contentProducer}, based on target

           *         template, target language and optional page parameters.

           */

          @Override

          public String getUrl(final ContentProducer contentProducer, final TemplateSet templateSet, final Language language,

                              final PageParams pageParams) {

                    final String name = getName(contentProducer, templateSet, language, pageParams);

                    final String extension = contentProducer.getExtension(templateSet);

                    int len = name.length();

                    if (!extension.isEmpty()) {

                              len += extension.length();

                              len++; // for dot

                    }

                    final StringBuilder buffer = new StringBuilder(0);

                    final String path = _pathLookup.lookupPath(contentProducer, language, templateSet);

                    if (path != null) {

                              buffer.ensureCapacity(len + path.length() + 2);

                              buffer.append('/');

                              if (!path.isEmpty()) {

                                        buffer.append(path);

                                        buffer.append('/');

                              }

                    } else {

                              collectPath(contentProducer.getParent(), language, templateSet, len, buffer);

                    }

                    buffer.append(name);

                    if (!extension.isEmpty()) {

                              buffer.append('.');

                              buffer.append(extension);

                    }

                    return buffer.toString();

          }

          /**

           * Build the URL for a Media Store element.

           *

           * @param node

           *            The target node, located in the Media Store.

           * @param language

           *            Target language or {@code null} for language-independent media

           *            nodes.

           * @param resolution

           *            Target resolution or {@code null} for media nodes of type

           *            {@link de.espirit.firstspirit.access.store.mediastore.Media#FILE}

           *            .

           * @return The URL for the {@code node}, based on target language and

           *         optional resolution.

           */

          @Override

          public String getUrl(final Media node, @Nullable final Language language, @Nullable final Resolution resolution) {

                    final String name = getName(node, language);

                    String resolutionString = null;

                    int len = name.length();

                    if ((resolution != null) && (node.getType() == Media.PICTURE) && !resolution.isOriginal()) {

                              resolutionString = resolution.getUid();

                              len += resolutionString.length();

                              len++; // for underscoure

                    }

                    final String extension = getExtension(node, language, resolution);

                    if (extension != null) {

                              len += extension.length();

                              len++; // for dot

                    }

                    final StringBuilder buffer = new StringBuilder(0);

                    collectPath(node.getParent(), language, null, len, buffer);

                    buffer.append(name);

                    if (resolutionString != null) {

                              buffer.append('_');

                              buffer.append(resolutionString);

                    }

                    if (extension != null) {

                              buffer.append('.');

                              buffer.append(extension);

                    }

                    return buffer.toString();

          }

          /**

           * Build a name for the provided node.

           *

           * This implementation first attempts to identify if the node is a page

           * reference that is used for content projection and displays only a single

           * dataset; if so, the sitemap variable is used to form a name.

           *

           * If the node is not used for content projection, but the following two

           * criteria are met: - the node is the start node of a Site Store folder -

           * the configuration parameter "usewelcomefilenames" evaluates to "yes" or

           * "true" (see {@code init(...)} the node is named "index".

           *

           * If none of the above criteria match, the language-dependent display name

           * of the node is retrieved and used as the node's name in the URL that is

           * being built.

           *

           * In all cases, the node names returned are processed using the

           * {@code cleanup(...)} method.

           *

           * @param contentProducer

           *            The node to identify a URL name for.

           * @param templateSet

           *            The template set for which to generate a URL name.

           * @param language

           *            The project language for which to generate a URL name.

           * @param pageParams

           *            Page parameters that may indicate if content projection is

           *            used.

           * @return The name of this {@code contentProducer}, to be used in forming a

           *         URL for this element.

           */

          private String getName(final ContentProducer contentProducer, final TemplateSet templateSet,

                              final Language language, final PageParams pageParams) {

                    if ((contentProducer instanceof PageRef) && (pageParams instanceof ContentPageParams)

                                        && (pageParams.getSize() == 1)) {

                              final Content2Params content2Params = ((PageRef) contentProducer).getContent2Params();

                              if (content2Params != null) {

                                        String varName = content2Params.getSitemapVariableName();

                                        if (varName != null) {

                                                  if (varName.endsWith("*")) {

                                                            varName = varName.substring(0, varName.length() - 1);

                                                  }

                                                  final ContentPageParams contentPageParams = (ContentPageParams) pageParams;

                                                  final List<Entity> list = contentPageParams.getData();

                                                  if (!list.isEmpty()) {

                                                            final Entity entity = list.get(0);

                                                            final String result = resolve(entity, varName, language, "");

                                                            if (!StringUtil.isEmpty(result)) {

                                                                      return cleanup(result);

                                                            }

                                                  }

                                        }

                              }

                    }

                    if (_useWelcomeFilenames && (pageParams.getIndex() == 0) && !(pageParams instanceof ContentPageParams)) {

                              final SiteStoreFolder folder = (SiteStoreFolder) contentProducer.getParent();

                              // noinspection ObjectEquality

                              if ((folder != null) && (folder.getStartNode() == contentProducer)) {

                                        // check if we are the first template set for a specific

                                        // extension

                                        final String extension = templateSet.getExtension();

                                        for (final TemplateSet set : contentProducer.getProject().getTemplateSets()) {

                                                  // noinspection ObjectEquality

                                                  if (set == templateSet) {

                                                            return "index";

                                                  }

                                                  if (extension.equals(set.getExtension())) {

                                                            break;

                                                  }

                                        }

                              }

                    }

                    final String name = cleanup(contentProducer.getFilename());

                    final String pageSuffix = pageParams.getPageSuffix();

                    if (!pageSuffix.isEmpty()) {

                              return name + '_' + pageSuffix;

                    }

                    return name;

          }

          /**

           * Build a name for the provided node. This implementation takes the

           * language-dependent name (see

           * {@link de.espirit.firstspirit.access.store.IDProvider#getLanguageInfo(Language)}

           * ). If this is not set for the provided language, the

           * {@link de.espirit.firstspirit.access.project.Project#getMasterLanguage()

           * project master language} is used. If this is also not set, the

           * {@link de.espirit.firstspirit.access.store.IDProvider#getUid() uid of the

           * node } is used. Then leading and trailing chars are stripped and some

           * chars with special meaning in URLs and file names are replaced by '-'

           * (see {@link #cleanup(String)}).

           *

           * @param node

           *            Get the name for this node.

           * @param language

           *            Get the name for this language.

           * @return Name part of path for provided node and language.

           */

          private String getName(final IDProvider node, final Language language) {

                    LanguageInfo languageInfo = node.getLanguageInfo(language);

                    String displayName = languageInfo != null ? languageInfo.getDisplayName() : null;

                    if (displayName != null) {

                              final String cleaned = cleanup(displayName);

                              if (!cleaned.isEmpty()) {

                                        return cleaned;

                              }

                    }

                    final Language masterLanguage = node.getProject().getMasterLanguage();

                    // noinspection ObjectEquality

                    if (masterLanguage != language) {

                              languageInfo = node.getLanguageInfo(masterLanguage);

                              if (languageInfo != null) {

                                        displayName = languageInfo.getDisplayName();

                                        if (displayName != null) {

                                                  final String cleaned = cleanup(displayName);

                                                  if (!cleaned.isEmpty()) {

                                                            return cleaned;

                                                  }

                                        }

                              }

                    }

                    return cleanup(node.getUid());

          }

          /**

           * Recursive method building a slash-delimited path for the provided folder

           * a it's parent chain. For each folder on the chain this methods calls

           * {@link #getName(de.espirit.firstspirit.access.store.IDProvider,de.espirit.firstspirit.access.Language)

           * getName(folder, language)}. For the root folder (

           * <tt>{@link IDProvider#getParent() folder.getParent()} == null</tt>) the

           * constructed path is empty. The constructed path will be appended to the

           * provided {@link StringBuilder}. The constructed path will start and end

           * with a slash.

           *

           * @param folder

           *            folder for which the path from root is collected.

           * @param language

           *            language, will be forwarded to

           *            {@link #getName(IDProvider,Language)} to build the name of

           *            each path element

           * @param templateSet

           *            template set, piped through to

           *            {@link PathLookup#lookupPath(IDProvider, Language, TemplateSet)}

           *            - may be {@code null}.

           * @param length

           *            size estimation for the StringBuilder, will be increased in

           *            every call, used to {@link StringBuilder#ensureCapacity(int)

           *            ensure its capacity} to prevent frequent resizing

           * @param collector

           *            the builded path is appended to this instance

           */

          final void collectPath(final IDProvider folder, final Language language, @Nullable final TemplateSet templateSet,

                              final int length, final StringBuilder collector) {

                    String name = _pathLookup.lookupPath(folder, language, templateSet);

                    if (name == null) {

                              name = getName(folder, language);

                              collectPath(folder.getParent(), language, templateSet, length + name.length() + 1, collector);

                              collector.append(name);

                              collector.append('/');

                    } else {

                              final int len = name.length();

                              if (len > 0) {

                                        collector.ensureCapacity(length + len + 2);

                                        if (name.charAt(0) != '/') {

                                                  collector.append('/');

                                        }

                                        collector.append(name);

                              } else {

                                        collector.ensureCapacity(length + 1);

                              }

                              collector.append('/');

                    }

          }

          /**

           * Helper to determine the extension (e.g. "gif", "pdf") for the given media

           * in the given {@code language} and {@code resolution}.

           * <p />

           *

           * Provided language may be {@code null} for language independent media

           * objects, resolution is null media objects of type {@link Media#FILE}.

           *

           * @param media

           *            Media node to get the extension for.

           * @param lang

           *            Language to get the extension for (is {@code  null} if provided

           *            media not isn't langage dependent).

           * @param resolution

           *            Resolution to get the extension for (is {@code  null} if

           *            provided media is of type {@link Media#FILE}).

           * @return File extension.

           */

          @Nullable

          String getExtension(final Media media, @Nullable final Language lang, @Nullable final Resolution resolution) {

                    if (media.getType() == Media.FILE) {

                              final File file = media.getFile(lang);

                              if (file == null) {

                                        throw new RuntimeException("no file data found for media:\"" + media.getUid() + "\" (id="

                                                            + media.getId() + ')');

                              }

                              return file.getExtension();

                    } else {

                              try {

                                        final Picture picture = media.getPicture(lang);

                                        if (picture == null) {

                                                  throw new RuntimeException("no picture data found for media:\"" + media.getUid() + "\" (id="

                                                                      + media.getId() + ')');

                                        }

                                        final MediaMetaData mediaMetaData = picture.getPictureMetaData(resolution);

                                        if (mediaMetaData == null) {

                                                  throw new RuntimeException("no picture data found for media:\"" + media.getUid() + "\" (id="

                                                                      + media.getId() + ')');

                                        }

                                        return mediaMetaData.getExtension();

                              } catch (final IOException e) {

                                        throw new IoError(e);

                              }

                    }

          }

          /**

           * Matcher for chars (with special meaning in URIs or not valid in windows

           * file names):

           * <ul>

           * <li>;</li>

           * <li>@</li>

           * <li>&amp;</li>

           * <li>=</li>

           * <li>+</li>

           * <li>$</li>

           * <li>,</li>

           * <li>/</li>

           * <li>\</li>

           * <li>&lt;</li>

           * <li>&gt;</li>

           * <li>:</li>

           * <li>*</li>

           * <li>|</li>

           * <li>#</li>

           * <li>?</li>

           * <li>"</li>

           * <li>whitespace</li>

           * </ul>

           *

           */

          //private static final Pattern SPECIAL_CHARS = Pattern

          //                    .compile("(_|;|@|&|=|\\+|\\$|,|/|\\\\|<|>|:|\\*|\\||#|\\?|\"|\\s|-)+");

          private static final Pattern SPECIAL_CHARS = Pattern

                              .compile("[^A-Za-z0-9.]+");

          /**

           * Strips leading and trailing whitespaces and replaces whitespaces and

           * chars wich are have a special meaning in URIs or filenames (e.g. under

           * Windows(TM)) with a single minus character.

           *

           * @param name

           *            String to clean up.

           * @return Cleaned string.

           */

          final String cleanup(String name) {

                    name = name.trim();

                    name = name.toLowerCase();

                    name = replaceMutatedVowels(name);

                    final Matcher matcher = SPECIAL_CHARS.matcher(name);

                    if (matcher.find()) {

                              String cleaned = matcher.replaceAll("-");

                              if (cleaned.length() == 1) {

                                        return cleaned;

                              }

                              if (cleaned.charAt(0) == '-') {

                                        cleaned = cleaned.substring(1);

                              }

                              final int length = cleaned.length();

                              if (cleaned.charAt(length - 1) == '-') {

                                        cleaned = cleaned.substring(0, length - 1);

                              }

                              return cleaned;

                    }

                    return name;

          }

          private static String resolve(Entity entity, final String varName, final Language language,

                              final String defaultLabel) {

                    String attribute = varName;

                    final String[] attributes = varName.split("\\.");

                    if (attributes.length > 1) {

                              final int lastIndex = attributes.length - 1;

                              for (int i = 0; i < lastIndex; i++) {

                                        final Object value = entity.getValue(attributes[i]);

                                        if (value instanceof Entity) {

                                                  entity = (Entity) value;

                                        } else {

                                                  return defaultLabel;

                                        }

                              }

                              attribute = attributes[lastIndex];

                    }

                    if (entity.getEntityType().getAttribute(attribute) == null) {

                              attribute = attribute + '_' + language.getAbbreviation();

                    }

                    final Object value = entity.getValue(attribute);

                    if (value == null || "".equals(value)) {

                              return defaultLabel;

                    }

                    return value.toString();

          }

          private String replaceMutatedVowels(String name) {

                    return name.replace("\u00FC", "ue").replace("\u00E4", "ae").replace("\u00F6","oe").replace("\u00df","ss");

          }

}

Seit dem Update auf FirstSpirit 5.2. können keine neuen Seiten mehr generiert werden - Fehlermeldung:

ERROR 23.03.2016 12:55:49.779 (de.espirit.firstspirit.generate.SiteProduction): generate of id=40007 failed - java.lang.IllegalArgumentException: de.espirit.firstspirit.store.access.sitestore.PageRefImpl

java.lang.IllegalArgumentException: de.espirit.firstspirit.store.access.sitestore.PageRefImpl

          at de.espirit.firstspirit.generate.PathLookup$1.lookupPath(PathLookup.java:52)

          at de.bgv.web.firstspirit.urlfactory.BGVUrlFactory.getUrl(BGVUrlFactory.java:86)

          at de.espirit.firstspirit.generate.path.RegistryUrlFactory.getUrl(RegistryUrlFactory.java:69)

          at de.espirit.firstspirit.generate.PluggableUrlCreator.getFilename(PluggableUrlCreator.java:185)

          at de.espirit.firstspirit.generate.SiteProduction.getFileHandle(SiteProduction.java:309)

          at de.espirit.firstspirit.generate.SiteProduction.generateFiles(SiteProduction.java:220)

          at de.espirit.firstspirit.generate.SiteProduction.render(SiteProduction.java:195)

          at de.espirit.firstspirit.generate.SiteProduction.render(SiteProduction.java:150)

          at de.espirit.firstspirit.generate.SiteProduction.start(SiteProduction.java:120)

          at de.espirit.firstspirit.generate.SiteProduction.start(SiteProduction.java:113)

          at de.espirit.firstspirit.server.scheduler.GenerateTaskExecutor.run(GenerateTaskExecutor.java:237)

          at de.espirit.firstspirit.server.scheduler.ScheduleManagerImpl$TaskCallable.executeLocal(ScheduleManagerImpl.java:2258)

          at de.espirit.firstspirit.server.scheduler.ScheduleManagerImpl$TaskCallable.executeLocal(ScheduleManagerImpl.java:2241)

          at de.espirit.firstspirit.server.scheduler.ScheduleManagerImpl$TaskCallable.call(ScheduleManagerImpl.java:2164)

          at de.espirit.firstspirit.server.ExecutionManagerImpl$ExtendedCallable.call(ExecutionManagerImpl.java:600)

          at java.util.concurrent.FutureTask.run(FutureTask.java:262)

          at de.espirit.common.util.BoundedExecutorService$RunnableWrapper.run(BoundedExecutorService.java:436)

          at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)

          at java.util.concurrent.FutureTask.run(FutureTask.java:262)

          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)

          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)

          at java.lang.Thread.run(Thread.java:745)

          at de.espirit.common.util.SuspendableThread.run(SuspendableThread.java:55)

Gibt es eine Möglichkeit das Problem - zumindest temporär - relativ einfach zu lösen, damit die Generierung mit den passenden URLs zumindest mal durchläuft.

Besten Dank und viele Grüße

Daniel

3 Replies
amelnik
I'm new here

Hallo zusammen,

genau das gleiche Problem tritt auch bei mir nach einem Update auf.(Server-Version: 5.2.311.72449 )

INFO  01.04.2016 16:37:36.173 (de.espirit.firstspirit.store.access.AccessStoreBuilder): PAGESTORE loaded in 1ms

INFO  01.04.2016 16:37:36.180 (de.udg.fs.HelukabelAdvUrl): Looking up path with arguments: _pathLookup=(class de.espirit.firstspirit.generate.PathLookup$1)de.espirit.firstspirit.generate.PathLookup$1@24405315contentProducer=(class de.espirit.firstspirit.store.access.sitestore.PageRefImpl)<PAGEREF editor="2859" htmlname="building_security" id="1673437" pageref="1672867" revision="4035652" uniquedescription="building_security">

          <LANG displayname="Building Security" language="ZA"/>

          <LANG displayname="Building Security" language="EN"/>

          <PAGE_LANG_SPEC language="ZA" showinpagegrp="1" showinsitemap="1"/>

</PAGEREF>

templateSet=(class de.espirit.firstspirit.server.templatemanagement.TemplateSetImpl)de.espirit.firstspirit.server.templatemanagement.TemplateSetImpl@7b0e9a4a{id=694183, name=html, presentationChannel=HTML}pageParams=(class de.espirit.firstspirit.access.store.PageParams)de.espirit.firstspirit.access.store.PageParams@839a5010

ERROR 01.04.2016 16:37:36.180 (de.espirit.firstspirit.generate.SiteProduction): generate of id=1673437 failed - java.lang.IllegalArgumentException: de.espirit.firstspirit.store.access.sitestore.PageRefImpl

java.lang.IllegalArgumentException: de.espirit.firstspirit.store.access.sitestore.PageRefImpl

          at de.espirit.firstspirit.generate.PathLookup$1.lookupPath(PathLookup.java:52)

          at de.udg.fs.HelukabelAdvUrl.getUrl(HelukabelAdvUrl.java:87)

...

Zusätzliche Info: Bei Seiten, die bereits vor dem update bestanden tritt der Fehler nicht auf, weil da wohl ja auch die URL gespeichert ist und deswegen auch nicht neu berechnet werden muss. Sobald man aber über Extras -> URL zurücksetzen diese Löscht kommt der o.g. Fehler auch bei diesen Seiten.

Wäre für eine schnelle Hilfe dankbar!

Grüße,

Alex

0 Kudos
amelnik
I'm new here

Hallo Daniel,

vom FirstSpirit Support, hab es einen Hinweis, dass die lookupPath() methode nur noch mit Ordnern arbeitet. Wenn man hier also eine PageRefImpl übergibt gibt es einen fehler.

Bei mir hat jetzt ein kleiner Fix bereits ausgereicht. Schau mal ob das für dich in Frage kommt:

Einfach

_pathLookup.lookupPath(contentProducer.getParent(), language, templateSet);

statt

_pathLookup.lookupPath(contentProducer, language, templateSet);

Grüße,

Alex

0 Kudos

Hi Alex,

danke für den Hinweis. Ich hatte auch vom Support den Hinweis erhalten und auch einen Fix bei uns in der Klasse eingebaut, so dass es wieder funktioniert.

Ich habe allerdings abgefragt von welchem Typ das Objekt ist...

if (contentProducer instanceof MediaFolder || contentProducer instanceof SiteStoreFolder) {    

    path = _pathLookup.lookupPath(contentProducer, language, templateSet);

}

Funtioniert bei uns.

Viele Grüße

Daniel

0 Kudos