8.7 Installation guide

Introduction

The following installation guide for the Goobi viewer refers to Ubuntu Linux 16.04. It is written as a step-by-step guide from top to bottom, meaning that settings and configurations build on each other. If the order is not followed, certain commands may fail.

The domain name used in this manual is VIEWER.EXAMPLE.ORG and must be adapted to your own DNS name.

Preparation

First, log on to the server on which the Goobi viewer is to be installed and obtain root rights:

ssh VIEWER.EXAMPLE.ORG
sudo -i

Then generate a password for the Goobi viewer database and a token and store them as a variable in the session. The DNS name is also stored there:

export PW_SQL_VIEWER=SECRETPASSWORT
export TOKEN=$(uuidgen)
export VIEWER_HOSTNAME=VIEWER.EXAMPLE.ORG

Now install the following packages:

apt -y install git maven

Finally, a temporary installation directory must be created and the Goobi viewer Connector, Goobi viewer Indexer and Goobi viewer Core Config Repository cloned. This includes the files required for the installation:

install=/tmp/install
mkdir -p $install
cd $install
git clone https://github.com/intranda/goobi-viewer-connector.git
git clone https://github.com/intranda/goobi-viewer-indexer.git
git clone https://github.com/intranda/goobi-viewer-core-config.git

It is recommended to have set a DNS entry for the server at this time.

Variables and aliases

Add the following aliases to /root/.bash_aliases:

cat << "EOF" >/root/.bash_aliases
alias cata='tail -n 1000 -f /var/log/tomcat8/catalina.out'
alias ct='chown tomcat8:tomcat8 *'
alias ctr='chown -R tomcat8:tomcat8 *'
alias ind='tail -n 1000 -f /opt/digiverso/logs/indexer.log'
alias vl='tail -n 1000 -f /opt/digiverso/logs/viewer.log'
EOF

Adopt the changes in the current session:

. /root/.bashrc

Install packages

Install the following packages including all dependencies:

apt -y install openjdk-8-jdk-headless tomcat8 mariadb-server apache2 ttf-mscorefonts-installer unzip

Configure services

Tomcat

In /etc/default/tomcat8, adjust the memory under -Xmx to the available machine memory, select reasonable garbage collector options and use urandom for faster Tomcat start:

patch /etc/default/tomcat8 << "EOF"
--- /etc/default/tomcat8 2018-03-01 19:11:46.947240791 +0000
+++ tomcat8 2018-03-01 19:14:01.920680439 +0000
@@ -18,7 +18,13 @@
# response time). If you use that option and you run Tomcat on a machine with
# exactly one CPU chip that contains one or two cores, you should also add
# the "-XX:+CMSIncrementalMode" option.
-JAVA_OPTS="-Djava.awt.headless=true -Xmx128m -XX:+UseConcMarkSweepGC"
+JAVA_OPTS="-Djava.awt.headless=true -Xmx4g -Xms2g"
+JAVA_OPTS="${JAVA_OPTS} -XX:+UseG1GC"
+JAVA_OPTS="${JAVA_OPTS} -XX:+ParallelRefProcEnabled"
+JAVA_OPTS="${JAVA_OPTS} -XX:+DisableExplicitGC"
+JAVA_OPTS="${JAVA_OPTS} -XX:+CMSClassUnloadingEnabled"
+JAVA_OPTS="${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom"
+JAVA_OPTS="${JAVA_OPTS} -Dfile.encoding='utf-8'"
# To enable remote debugging uncomment the following line.
# You will then be able to use a java debugger on port 8000.
@@ -32,9 +39,9 @@
#TOMCAT8_SECURITY=no
# Number of days to keep logfiles in /var/log/tomcat8. Default is 14 days.
-#LOGFILE_DAYS=14
+LOGFILE_DAYS=14
# Whether to compress logfiles older than today's
-#LOGFILE_COMPRESS=1
+LOGFILE_COMPRESS=1
# Location of the JVM temporary directory
# WARNING: This directory will be destroyed and recreated at every startup !
EOF

Further configurations are made in /etc/tomcat8/server.xml. This will disable the appContextProtection for the JreMemoryLeakPreventionListener, set up a HTTP connector (with correct proxyName) and an AJP connector on localhost. A Crawler Session Manager Valve is also activated:

sed -e "s|VIEWER.EXAMPLE.ORG|${VIEWER_HOSTNAME}|g" << "EOF" | patch /etc/tomcat8/server.xml
--- /etc/tomcat8/server.xml 2017-09-28 14:56:31.000000000 +0100
+++ server.xml 2018-03-01 19:18:50.143658964 +0000
@@ -29,7 +29,7 @@
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
-->
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
- <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
+ <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" appContextProtection="false" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
@@ -68,33 +68,18 @@
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
- <Connector port="8080" protocol="HTTP/1.1"
- connectionTimeout="20000"
- URIEncoding="UTF-8"
- redirectPort="8443" />
- <!-- A "Connector" using the shared thread pool-->
- <!--
- <Connector executor="tomcatThreadPool"
- port="8080" protocol="HTTP/1.1"
- connectionTimeout="20000"
- redirectPort="8443" />
- -->
- <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443
- This connector uses the NIO implementation that requires the JSSE
- style configuration. When using the APR/native implementation, the
- OpenSSL style configuration is required as described in the APR/native
- documentation -->
- <!--
- <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
- maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
- clientAuth="false" sslProtocol="TLS" />
- -->
-
- <!-- Define an AJP 1.3 Connector on port 8009 -->
- <!--
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
- -->
-
+<Connector address="127.0.0.1" port="8080" protocol="HTTP/1.1"
+ connectionTimeout="20000"
+ maxThreads="400"
+ URIEncoding="UTF-8"
+ enableLookups="false"
+ disableUploadTimeout="true"
+ proxyName="VIEWER.EXAMPLE.ORG"
+ proxyPort="80" />
+
+<Connector address="127.0.0.1" port="8009" protocol="AJP/1.3"
+ URIEncoding="UTF-8"
+ redirectPort="8443" />
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
@@ -137,10 +122,12 @@
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
- <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
+ <!--Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
- pattern="%h %l %u %t &quot;%r&quot; %s %b" />
-
+ pattern="%h %l %u %t &quot;%r&quot; %s %b" /-->
+ <Valve className="org.apache.catalina.valves.CrawlerSessionManagerValve"
+ crawlerUserAgents=".*[bB]ot.*|.*Yahoo! Slurp.*|.*Feedfetcher-Google.*|.*Apache-HttpClient.*|.*[Ss]pider.*|.*[Cc]rawler.*|.*nagios.*|.*Yandex.*"
+ sessionInactiveInterval="60"/>
</Host>
</Engine>
</Service>
EOF

Disable session persistence in /etc/tomcat8/context.xml by running the following patch:

patch /etc/tomcat8/context.xml << "EOF"
--- /etc/tomcat8/context.xml 2014-01-24 17:17:30.000000000 +0000
+++ context.xml 2018-03-01 19:23:05.166223891 +0000
@@ -24,9 +24,7 @@
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
- <!--
<Manager pathname="" />
- -->
<!-- Uncomment this to enable Comet connection tacking (provides events
on session expiration as well as webapp lifecycle) -->
EOF

Apache

Apache must be set up to make the viewer available externally. Activate the following modules for this:

a2enmod proxy_ajp
a2enmod rewrite
a2enmod expires
a2enmod headers

Place the following file at /etc/apache2/sites-available/${VIEWER_HOSTNAME}.conf:

sed -e "s|VIEWER.EXAMPLE.ORG|${VIEWER_HOSTNAME}|g" << "EOF" >/etc/apache2/sites-available/${VIEWER_HOSTNAME}.conf
<VirtualHost *:80>
ServerAdmin support@intranda.com
ServerName VIEWER.EXAMPLE.ORG
DocumentRoot /var/www
## make sure rewrite is enabled
RewriteEngine On
## search engines: do not follow certain urls
RewriteCond %{HTTP_USER_AGENT} ^.*bot.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*Yandex.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*spider.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*rawler.*$ [NC]
ReWriteRule ^(.*);jsessionid=[A-Za-z0-9]+(.*)$ $1$2 [L,R=301]
RewriteCond %{HTTP_USER_AGENT} ^.*bot.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*Yandex.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*spider.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*rawler.*$ [NC]
ReWriteRule ^(.*)viewer/!(.*)$ $1viewer/$2 [L,R=301]
RewriteCond %{HTTP_USER_AGENT} ^.*bot.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*Yandex.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*spider.*$ [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^.*rawler.*$ [NC]
ReWriteRule ^(.*)/[Ll][Oo][Gg]_(.*)$ $1/ [L,R=301]
## compress output
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain text/html text/xml
AddOutputFilterByType DEFLATE text/css text/javascript
AddOutputFilterByType DEFLATE application/xml application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript application/x-javascript
</IfModule>
## general proxy settings
ProxyPreserveHost On
SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1
<Proxy *>
Require local
</Proxy>
## CORS for IIIF
Header set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "GET, OPTIONS"
Header always set Access-Control-Max-Age "600"
Header always set Access-Control-Allow-Headers "Authorization, Content-Type"
Header always set Access-Control-Expose-Headers "Content-Security-Policy, Location"
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
# make sure ETag headers are forwarded correctly
# Post Apache 2.4 have a look at
# https://httpd.apache.org/docs/trunk/mod/mod_deflate.html#deflatealteretag
RequestHeader edit "If-None-Match" '(.*)-gzip"$' '$1", $1-gzip"'
## Viewer
redirect 301 /index.html http://VIEWER.EXAMPLE.ORG/viewer/
redirect 301 /viewer http://VIEWER.EXAMPLE.ORG/viewer/
RewriteRule ^/viewer/oai/oai2.xsl$ http://VIEWER.EXAMPLE.ORG/viewer/oai2.xsl
ProxyPassMatch ^/viewer/(oai.*)$ ajp://localhost:8009/M2M/$1 retry=0
<LocationMatch ^/viewer/(oai.*)$>
Forcetype text/xml
ProxyPassReverse ajp://localhost:8009/M2M/$1
</LocationMatch>
ProxyPassMatch ^/viewer/(sru.*)$ ajp://localhost:8009/M2M/$1 retry=0
<LocationMatch ^/viewer/(sru.*)$>
Forcetype text/xml
ProxyPassReverse ajp://localhost:8009/M2M/$1
</LocationMatch>
ProxyPassMatch ^/viewer/(.*)$ ajp://localhost:8009/viewer/$1 retry=0
<LocationMatch ^/viewer/(.*)$>
ProxyPassReverse ajp://localhost:8009/viewer/$1
<IfModule mod_expires.c>
ExpiresActive on
ExpiresByType image/jpg "access plus 1 months"
ExpiresByType image/gif "access plus 1 months"
ExpiresByType image/jpeg "access plus 1 months"
ExpiresByType image/png "access plus 1 months"
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType application/x-font-woff "access plus 1 year"
ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
</IfModule>
Require all granted
</LocationMatch>
## solr
redirect 301 /solr http://VIEWER.EXAMPLE.ORG/solr/
<Location /solr/>
Require local
ProxyPass ajp://localhost:8009/solr/ retry=0
ProxyPassReverse ajp://localhost:8009/solr/
</Location>
## logging
CustomLog /var/log/apache2/VIEWER.EXAMPLE.ORG_access.log combined
ErrorLog /var/log/apache2/VIEWER.EXAMPLE.ORG_error.log
# Possible values include: debug, info, notice, warn, error, crit, alert, emerg.
LogLevel warn
</VirtualHost>
EOF

Enable the Goobi viewer vhost, disable the default vhost and restart the Apache web server:

a2dissite 000-default
a2ensite ${VIEWER_HOSTNAME}.conf
systemctl restart apache2.service

Finally create a robots.txt:

sed -e "s|VIEWER.EXAMPLE.ORG|${VIEWER_HOSTNAME}|g" << "EOF" >/var/www/robots.txt
User-agent: *
Disallow: /viewer/content*action=pdf
Disallow: /viewer/search/
Disallow: /viewer/tags/
Disallow: /viewer/term/
Disallow: /viewer/oai
Disallow: /viewer/nextHit/
Disallow: /viewer/prevHit/
#Sitemap: http://VIEWER.EXAMPLE.ORG/viewer/sitemap_index.xml
Crawl-delay: 10
EOF

MySQL / MariaDB

Goobi viewer requires a database and its own user. This is created with the following command:

mysql -e "CREATE DATABASE viewer;
CREATE USER 'viewer'@'localhost' IDENTIFIED BY '$PW_SQL_VIEWER';
GRANT ALL PRIVILEGES ON viewer.* TO 'viewer'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;"

The database schema is created automatically the first time the application is started.

NFS

This point is only relevant if Goobi workflow is/is also installed and this is not done on the same machine.

Then the /opt/digiverso/viewer folder must be exported to the Goobi workflow server. NFS is used for this. The adjustments for this are here:

export IP_GOOBI=1.2.3.4 # IP-Adresse des Goobi workflow Servers
apt install nfs-kernel-server -y
echo "/opt/digiverso/viewer/hotfolder ${IP_GOOBI}/255.255.255.255(rw,sync,no_subtree_check,all_squash,anonuid=$(id -u tomcat8),anongid=$(id -g tomcat8))" >> /etc/exports
systemctl restart nfs-kernel-server.service

The port for NFSv4 is TCP 2049. If UFW is used:

ufw allow from $IP_GOOBI proto tcp to any port 2049

The adjustments for Goobi workflow can be found in the installation instructions there:

Create directory structure

The following commands create the necessary folder structure:

mkdir -p /opt/digiverso/{config/bin,indexer,logs,tomcat-lib,viewer/{abbyy,cmdi,deleted_mets,hotfolder,media,orig_lido,orig_denkxweb,success,ugc,alto,cms_media,error_mets,indexed_lido,mix,pdf,tei,updated_mets,cache,config/{PDFTitlePage,watermark},fulltext,indexed_mets,oai/token,ptif,themes,wc,bin}}
chown -R tomcat8: /opt/digiverso

Installation

The installation of the required components is described below.

Apache Solr

Download and unzip the latest Solr 4.x version:

wget -O $install/solr.tgz http://archive.apache.org/dist/lucene/solr/4.10.4/solr-4.10.4.tgz
tar -C $install -xzf $install/solr.tgz

Move the example/solr folder to /opt/digiverso/viewer/apache-solr:

mkdir -p /opt/digiverso/viewer/apache-solr/
mv $install/solr-4.10.4/example/solr/* /opt/digiverso/viewer/apache-solr/

In solr.xml, comment out the entire solrCloud block and configure a core:

patch /opt/digiverso/viewer/apache-solr/solr.xml << "EOF"
--- /opt/digiverso/viewer/apache-solr/solr.xml 2014-09-08 09:56:09.000000000 +0200
+++ /tmp/solr.xml 2018-04-17 17:11:08.624242625 +0200
@@ -26,17 +26,13 @@
http://wiki.apache.org/solr/CoreAdmin
-->
<solr>
- <solrcloud>
- <str name="host">${host:}</str>
- <int name="hostPort">${jetty.port:8983}</int>
- <str name="hostContext">${hostContext:solr}</str>
- <int name="zkClientTimeout">${zkClientTimeout:30000}</int>
- <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
- </solrcloud>
+ <cores adminPath="/admin/cores" defaultCoreName="collection1">
+ <core name="collection1" instanceDir="collection1" />
+ </cores>
<shardHandlerFactory name="shardHandlerFactory"
class="HttpShardHandlerFactory">
<int name="socketTimeout">${socketTimeout:0}</int>
<int name="connTimeout">${connTimeout:0}</int>
EOF

Move the required libraries:

mkdir -p /opt/digiverso/viewer/apache-solr/lib
mv -t /opt/digiverso/viewer/apache-solr/lib $install/solr-4.10.4/contrib/analysis-extras/lucene-libs/lucene-analyzers-icu-4.10.4.jar $install/solr-4.10.4/contrib/analysis-extras/lib/icu4j-53.1.jar $install/solr-4.10.4/dist/solr-analysis-extras-4.10.4.jar

Takeover of solr.war:

mv $install/solr-4.10.4/dist/solr-4.10.4.war /opt/digiverso/viewer/apache-solr/solr.war

Customize collection1/conf/solrconfig.xml:

patch /opt/digiverso/viewer/apache-solr/collection1/conf/solrconfig.xml << "EOF"
--- /opt/digiverso/viewer/apache-solr/collection1/conf/solrconfig.xml 2015-02-27 19:09:58.000000000 +0100
+++ /tmp/solrconfig.xml 2018-04-17 17:21:23.353907791 +0200
@@ -70,19 +70,9 @@
is found that matches, a warning will be logged.
The examples below can be used to load some solr-contribs along
with their external dependencies.
-->
- <lib dir="${solr.install.dir:../../..}/contrib/extraction/lib" regex=".*\.jar" />
- <lib dir="${solr.install.dir:../../..}/dist/" regex="solr-cell-\d.*\.jar" />
-
- <lib dir="${solr.install.dir:../../..}/contrib/clustering/lib/" regex=".*\.jar" />
- <lib dir="${solr.install.dir:../../..}/dist/" regex="solr-clustering-\d.*\.jar" />
-
- <lib dir="${solr.install.dir:../../..}/contrib/langid/lib/" regex=".*\.jar" />
- <lib dir="${solr.install.dir:../../..}/dist/" regex="solr-langid-\d.*\.jar" />
-
- <lib dir="${solr.install.dir:../../..}/contrib/velocity/lib" regex=".*\.jar" />
- <lib dir="${solr.install.dir:../../..}/dist/" regex="solr-velocity-\d.*\.jar" />
+ <lib dir="../lib" />
<!-- an exact 'path' can be used instead of a 'dir' to specify a
specific jar file. This will cause a serious error to be logged
@@ -97,11 +87,11 @@
Used to specify an alternate directory to hold all index data
other than the default ./data under the Solr home. If
replication is in use, this should match the replication
configuration.
-->
- <dataDir>${solr.data.dir:}</dataDir>
+ <dataDir>${solr.data.dir:/opt/digiverso/viewer/apache-solr/collection1/data}</dataDir>
<!-- The DirectoryFactory to use for indexes.
solr.StandardDirectoryFactory is filesystem
@@ -838,11 +828,11 @@
will be overridden by parameters in the request
-->
<lst name="defaults">
<str name="echoParams">explicit</str>
<int name="rows">10</int>
- <str name="df">text</str>
+ <str name="df">DEFAULT</str>
</lst>
<!-- In addition to defaults, "appends" params can be specified
to identify values which should be appended to the list of
multi-val params from the query (or the existing "defaults").
-->
@@ -900,11 +890,11 @@
<requestHandler name="/query" class="solr.SearchHandler">
<lst name="defaults">
<str name="echoParams">explicit</str>
<str name="wt">json</str>
<str name="indent">true</str>
- <str name="df">text</str>
+ <str name="df">DEFAULT</str>
</lst>
</requestHandler>
<!-- realtime get handler, guaranteed to return the latest stored fields of
@@ -971,11 +961,11 @@
<str name="defType">edismax</str>
<str name="qf">
text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4
title^10.0 description^5.0 keywords^5.0 author^2.0 resourcename^1.0
</str>
- <str name="df">text</str>
+ <str name="df">DEFAULT</str>
<str name="mm">100%</str>
<str name="q.alt">*:*</str>
<str name="rows">10</str>
<str name="fl">*,score</str>
@@ -1403,11 +1393,11 @@
See http://wiki.apache.org/solr/SpellCheckComponent for details
on the request parameters.
-->
<requestHandler name="/spell" class="solr.SearchHandler" startup="lazy">
<lst name="defaults">
- <str name="df">text</str>
+ <str name="df">DEFAULT</str>
<!-- Solr will use suggestions from both the 'default' spellchecker
and from the 'wordbreak' spellchecker and combine them.
collations (re-written queries) can include a combination of
corrections from both spellcheckers -->
<str name="spellcheck.dictionary">default</str>
@@ -1463,11 +1453,11 @@
In reality you will likely want to add the component to your
already specified request handlers.
-->
<requestHandler name="/tvrh" class="solr.SearchHandler" startup="lazy">
<lst name="defaults">
- <str name="df">text</str>
+ <str name="df">DEFAULT</str>
<bool name="tv">true</bool>
</lst>
<arr name="last-components">
<str>tvComponent</str>
</arr>
@@ -1609,11 +1599,11 @@
<!-- A request handler for demonstrating the elevator component -->
<requestHandler name="/elevate" class="solr.SearchHandler" startup="lazy">
<lst name="defaults">
<str name="echoParams">explicit</str>
- <str name="df">text</str>
+ <str name="df">DEFAULT</str>
</lst>
<arr name="last-components">
<str>elevator</str>
</arr>
</requestHandler>
EOF

Transfer of the Solr schema from the Goobi viewer Indexer Repository:

mv $install/goobi-viewer-indexer/goobi-viewer-indexer/src/main/resources/other/schema.xml /opt/digiverso/viewer/apache-solr/collection1/conf/

Linking the stopwords file:

ln --relative -s /opt/digiverso/viewer/apache-solr/collection1/conf/lang/stopwords_de.txt /opt/digiverso/viewer/apache-solr/collection1/conf/lang/stopwords.txt

Configuration of logging (see also here):

mv $install/solr-4.10.4/example/lib/ext/*.jar /opt/digiverso/tomcat-lib/

Solr in Tomcat:

cat << "EOF" >/etc/tomcat8/Catalina/localhost/solr.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="/opt/digiverso/viewer/apache-solr/solr.war" crossContext="true">
<Environment name="solr/home" type="java.lang.String" value="/opt/digiverso/viewer/apache-solr" override="true"/>
<Resources>
<PreResources
className="org.apache.catalina.webresources.DirResourceSet"
base="/opt/digiverso/tomcat-lib/"
webAppMount="/WEB-INF/lib" />
</Resources>
</Context>
EOF

Finally, create a directory for the data and adjust the rights:

mkdir /opt/digiverso/viewer/apache-solr/collection1/data/
chown -R tomcat8: /opt/digiverso/viewer/apache-solr

Goobi viewer Indexer

First the application is compiled. Then copy the required files to /opt/digiverso/indexer/ and rename the indexerconfig_solr.xml to solr_indexerconfig.xml, control the Tomcat port and replace C: with /opt if necessary. Finally the systemd service unit is activated:

mvn -f $install/goobi-viewer-indexer/goobi-viewer-indexer package
mv -t /opt/digiverso/indexer/ $install/goobi-viewer-indexer/goobi-viewer-indexer/target/solrIndexer.jar
sed -e 's/8081/8080/g' -e 's/C:/\/opt/g' $install/goobi-viewer-indexer/goobi-viewer-indexer/target/classes/indexerconfig_solr.xml -e 's/collection2/collection1/g' > /opt/digiverso/indexer/solr_indexerconfig.xml
mv $install/goobi-viewer-indexer/goobi-viewer-indexer/src/main/resources/other/solrindexer.service /etc/systemd/system/
systemctl enable solrindexer.service

Goobi viewer

Stop the Tomcat service:

systemctl stop tomcat8

Then put the following file to /etc/tomcat8/Catalina/localhost/viewer.xml:

sed -e "s|PW_SQL_VIEWER|${PW_SQL_VIEWER}|g" << "EOF" >/etc/tomcat8/Catalina/localhost/viewer.xml
<?xml version='1.0' encoding='UTF-8'?>
<Context>
<Resources>
<PreResources
className="org.apache.catalina.webresources.DirResourceSet"
base="/opt/digiverso/tomcat-lib/"
webAppMount="/WEB-INF/lib" />
<PreResources
className="org.apache.catalina.webresources.DirResourceSet"
base="/opt/digiverso/viewer/themes/goobi-viewer-theme-reference/goobi-viewer-theme-reference/WebContent/resources/themes/"
webAppMount="/resources/themes" />
</Resources>
<Resource
name="viewer"
auth="Container"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
type="javax.sql.DataSource"
driverClassName="org.mariadb.jdbc.Driver"
username="viewer"
password="PW_SQL_VIEWER"
maxActive="100"
maxIdle="30"
minIdle="4"
maxWait="10000"
testOnBorrow="true"
testWhileIdle="true"
validationQuery="SELECT SQL_NO_CACHE 1"
removeAbandoned="true"
removeAbandonedTimeout="600"
url="jdbc:mysql://localhost/viewer?characterEncoding=UTF-8&amp;autoReconnect=true&amp;autoReconnectForPools=true" />
</Context>
EOF

Now adjust the file rights so that the file is not deleted with an update:

chown -R root.tomcat8 /etc/tomcat8/Catalina
chmod -R g-w /etc/tomcat8/Catalina

The theme is integrated as an external theme:

mkdir -p /opt/digiverso/viewer/themes/
cd /opt/digiverso/viewer/themes/
git clone https://github.com/intranda/goobi-viewer-theme-reference.git

Now compile the Goobi viewer and move it to the expected location in the file system:

mvn -f /opt/digiverso/viewer/themes/goobi-viewer-theme-reference/goobi-viewer-theme-reference package
mv /opt/digiverso/viewer/themes/goobi-viewer-theme-reference/goobi-viewer-theme-reference/target/viewer.war /var/lib/tomcat8/webapps/viewer.war

Finally move the last configuration files and restart the Tomcat service:

mv $install/goobi-viewer-core-config/goobi-viewer-core-config/src/main/resources/install/* /opt/digiverso/viewer/config/
chown -R tomcat8: /opt/digiverso/viewer/
systemctl start tomcat8

Connector - (OAI & SRU)

For the installation of the OAI and SRU interface the application has to be compiled first:

mvn -f $install/goobi-viewer-connector/goobi-viewer-connector package

After that the war file and the configuration files have to be moved to the expected location:

mv $install/goobi-viewer-connector/goobi-viewer-connector/target/M2M.war /opt/digiverso/viewer/bin/M2M.war
mv $install/goobi-viewer-connector/goobi-viewer-connector/target/M2M/WEB-INF/classes/*.xsl /opt/digiverso/viewer/oai/
chown -R tomcat8: /opt/digiverso/viewer/bin/
chown -R tomcat8: /opt/digiverso/viewer/oai/

Then put the following file to /etc/tomcat8/Catalina/localhost/M2M.xml:

cat << "EOF" >/etc/tomcat8/Catalina/localhost/M2M.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/M2M"
docBase="/opt/digiverso/viewer/bin/M2M.war"
crossContext="true"
reloadable="true">
</Context>
EOF

Extraction of config_oai.xml:

unzip -p /opt/digiverso/viewer/bin/M2M.war WEB-INF/classes/config_oai.xml > /opt/digiverso/viewer/config/config_oai.xml

Adjust the settings to the correct URL so that the resolver links work and ensure that the port of the tomcat on which the Solr service is running is 8080:

sed -i -e "s|http://localhost:8080/viewer/oai|http://${VIEWER_HOSTNAME}/viewer/oai|g" -e "s|http://localhost:8080/viewer/resolver|http://${VIEWER_HOSTNAME}/viewer/resolver|g" -e "s|http://localhost:8080/viewer/piresolver|http://${VIEWER_HOSTNAME}/viewer/piresolver|g" -e "s|http://localhost:8081/solr|http://localhost:8080/solr|g" /opt/digiverso/viewer/config/config_oai.xml

Settings

Cronjob

A cronjob must be set up for regular tasks:

sed -e "s|VIEWER.EXAMPLE.ORG|${VIEWER_HOSTNAME}|g" -e "s|TOKEN|${TOKEN}|g" << "EOF" >/etc/cron.d/intranda-goobiviewer
PATH=/usr/bin:/bin:/usr/sbin/
MAILTO=support@intranda.com
#
# Regular cron jobs for the Goobi viewer
#
## This REST call triggers the email notification about new search hits for users,
## that enabled notifications for saved searches
42 8,12,17 * * * root curl -s http://localhost:8080/viewer/rest/search/sendnotifications/?token=TOKEN
## This REST call creates an XML sitemap for the Goobi viewer instance. Please always
## call it on it's external URL because otherwise the protocol (http/https) might not
## be detected correctly
18 1 * * * root curl -s -X POST -H "Content-Type: application/json" -d '{}' "http://VIEWER.EXAMPLE.ORG/viewer/rest/sitemap/update/?token=TOKEN" 1>/dev/null
## These two scripts pull the theme git repository regulary. The @daily part is only
## a reminder for the 1-minute schedule
#*/1 * * * * root cd /opt/digiverso/viewer/themes/goobi-viewer-theme-reference; git pull | grep -v -e "Already up.to.date." -e "Bereits aktuell."
#@daily root echo "Please look at the git checkout interval for the Goobi viewer theme" | mail -s "Reference: Theme repository is checked out every minute" support@intranda.com
## Optimize the Solr search index once a month
@monthly root curl -s http://localhost:8080/solr/update?optimize=true&waitFlush=false
EOF

config_viewer.xml

In the local config_viewer.xml different settings have to be stored:

sed -e "s|VIEWER.EXAMPLE.ORG|${VIEWER_HOSTNAME}/viewer|g" -e "s|TOKEN|${TOKEN}|g" << "EOF" >/opt/digiverso/viewer/config/config_viewer.xml
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<urls>
<metadata>
<mets>http://VIEWER.EXAMPLE.ORG/oai?verb=GetRecord&amp;metadataPrefix=mets&amp;identifier=</mets>
<marc>http://VIEWER.EXAMPLE.ORG/oai?verb=GetRecord&amp;metadataPrefix=marcxml&amp;identifier=</marc>
<dc>http://VIEWER.EXAMPLE.ORG/oai?verb=GetRecord&amp;metadataPrefix=oai_dc&amp;identifier=</dc>
<ese>http://VIEWER.EXAMPLE.ORG/oai?verb=GetRecord&amp;metadataPrefix=europeana&amp;identifier=</ese>
</metadata>
<contentServerWrapper>http://VIEWER.EXAMPLE.ORG/content/</contentServerWrapper>
<download>http://VIEWER.EXAMPLE.ORG/download/</download>
<rest>http://VIEWER.EXAMPLE.ORG/rest/</rest>
</urls>
<viewer>
<theme subTheme="false" mainTheme="reference" discriminatorField="" autoSwitch="true" addFilterQuery="false" filterQueryVisible="false">
<rootPath>/opt/digiverso/goobi-viewer-theme-reference/goobi-viewer-theme-reference/WebContent/resources/themes/</rootPath>
</theme>
</viewer>
<rss>
<numberOfItems>50</numberOfItems>
<title>Goobi viewer RSS Feed</title>
<description>new items</description>
<copyright>(c) Goobi viewer using institution </copyright>
</rss>
<webapi>
<authorization>
<token>TOKEN</token>
</authorization>
</webapi>
</config>
EOF

Create user account

The Goobi viewer has a backend. The following command inserts a test account with the user name goobi@intranda.com and the password viewer into the database:

mysql -e 'USE viewer;
INSERT INTO users (active,email,password_hash,score,superuser) VALUES (1,"goobi@intranda.com","$2a$10$Z5GTNKND9ZbuHt0ayDh0Remblc7pKUNlqbcoCxaNgKza05fLtkuYO",0,1);'