#!/bin/sh

# Repository Upgrade Script
#
# Run this after installing a new release of the repository webapp
# to automatically upgrade the repository contents
#
# See "usage" variable below for synopsis of arguments
#
# Note: All output is prefaced with either INFO, WARNING, or ERROR
#  so a script running this can simply grep for WARNING|ERROR to see
#  if there are any messages worth forwarding to a human.
#
# TODO:
#  - do svn diff to present changes between loaded and new prototypes in the
#    graphs (NG_Internal, query) where load-file is only the initializer
#    and user data is expected to accumulate..
#  - perhaps work out update protocol for NG_Internal driven by versions.
#
# $Id: upgrade.sh 3062 2010-07-15 22:46:18Z lcs14 $

#------- Constants
usage="Usage: $0  WAR-file-path  username  password  repo-URL-root, e.g. $0  ROOT.war bigbird passwird http://repo.university.edu/"

# repository namespace prefix
ns=http://eagle-i.org/ont/repo/1.0/

# owl namespace prefix
owl="http://www.w3.org/2002/07/owl#"

# named graphs
NG_INTERNAL=${ns}NG_Internal
NG_QUERY=${ns}NG_Query
REPO=$ns

# data model ontology graph URI:
DM=http://purl.obolibrary.org/obo/ero.owl

# relative paths in WAR
repoOntPath=WEB-INF/classes/repository-ont.n3
repoInternalPath=WEB-INF/classes/repository-internal.n3
queryMacrosPath=WEB-INF/classes/query-macros.n3

#------- Sanity-check args
warpath=$1
username=$2
password=$3
repoURL=$4
if [ $# -lt 4 ]; then
    echo "ERROR $usage"
    exit 1
fi
if echo "$repoURL"|egrep -qv '^https?:'; then
    echo "ERROR Repository URL must be a valid http(s) URL: \"$repoURL\""
    exit 1
fi
if [ ! -f "$warpath" ]; then
    echo "ERROR Cannot open WAR file: $warpath"
    exit 2
fi

# usage: graphURI  versionPropertyURI filename
# returns highest SCM version number loaded into graph
getGraphVersion () {
  local id0=`echo "SELECT * WHERE {<${1}> <${2}> ?version}" |\
    curl -s -u ${username}:$password --form-string 'format=text/plain' \
    -F "default-graph-uri=$1" -F "query=@-" \
    ${repoURL}/repository/sparql | tail +2`

  if [ -z "$id0" ]; then
    echo "ERROR failed to find version (${2}) statement in named graph: $1" 1>&2
    return 1
  fi

  ##echo "DEBUG got id=$id" 1>&2
  local id=`echo "$id0" | egrep -o '\\$Id:.*\\$'`
  if [ -z "$id" ]; then
    echo "ERROR There is no proper \$Id string in the version (${2}) property of named graph: ${1}, value=${id0}" 1>&2
    return 1
  fi

  # format of $Id: upgrade.sh 3062 2010-07-15 22:46:18Z lcs14 $ is e.g. $Id: upgrade.sh 3062 2010-07-15 22:46:18Z lcs14 $
  # NOTE: $id may contain multiple matches, awk must allow for that
  local fname=`echo "$id" | awk 'NR == 1 {print $2}'`
  if [ "$fname" != "$3" ]; then
    echo "ERROR bad version string: filename in graph, \"${fname}\", does not match \"${3}\"" 1>&2
    return 2
  fi
  # print the highest SCM revision number
  echo "$id" | awk '{ if ($3 > v) v = $3} END {print v}'
}

# usage:  relative-path
getFileVersion () {
    local id=`unzip -p "$warpath" $1 | egrep -o '\\$Id:.*\\$'`
    if [ -z "$id" ]; then
      echo "ERROR member file \"${1}\" in WAR file does not have an \$Id keyword."
      return 1
    fi
    local proto=`basename $1`
    local fname=`echo "$id" | awk '{print $2}'`
    if [ "$fname" != "$proto" ]; then
      echo "ERROR filename in file's \$Id, \"${fname}\", does not match \"${proto}\"" 1>&2
      return 2
    fi
    # print the SCM revision number
    echo "$id" | awk '{print $3}'
}

# usage: graphURI  versionPropertyURI relative-path-in-war
#  returns bool - true (0) if repo's graph is out of date, 1 if not,
#   or special value of 2 if there was an error getting data.
needUpdate () {
  local filename=`basename $3`
  local graphv=`getGraphVersion ${1} $2 $filename`
  local filev=`getFileVersion $3`
  if [ -z "$graphv" -o -z "$filev" ]; then
      echo "ERROR ${1}: failed to get version of either graph ($graphv) or file ($filev)"
      return 2
  else
      if [ "$graphv" -eq "$filev" ]; then
          echo "INFO SKIP: ${1}: live graph and sourcefile are same SCM version ($graphv)"
          return 1
      elif [ "$graphv" -gt "$filev" ]; then
          echo "WARNING ${1}: ANOMALY, graph version ($graphv) is LATER than file ($filev)."
          return 1
      else
          echo "INFO UPDATE: ${1}: sourcefile (${filev}) has later SCM version than graph ($graphv)"
          return 0
      fi
  fi
}

# compare two version strings of the form  0.4.9 and 0.4.3
versionLessThan () {
    local a=(`echo "$1" | tr '.' ' '`)
    local b=(`echo "$2" | tr '.' ' '`)
    for i in 0 1 2 3 4; do
        if [ -z "${b[$i]}" -a -n "${a[$i]}" ]; then
            return 1
        elif [ -z "${a[$i]}" -a -n "${b[$i]}" ]; then
            return 0
        # equal is not less than
        elif [ -z "${a[$i]}" -a -z "${b[$i]}" ]; then
            return 1
        elif [ "${a[$i]}" -gt "${b[$i]}" ]; then
            return 1
        elif [ "${a[$i]}" -lt "${b[$i]}" ]; then
            return 0
        fi
    done
    # they are equal
    return 1
}

# ------------ Procedure:

# 1. ------ repository's internal ontology with file
if needUpdate ${REPO} "${owl}versionInfo" $repoOntPath; then
    echo "INFO Reloading Repository Internal Ontology: $REPO"
    unzip -p "$warpath" $repoOntPath | curl  -s -u ${username}:$password \
      -F 'content=@-;type=text/rdf+n3' -F 'action=replace' \
      -F "name=${REPO}" "${repoURL}/repository/graph"
elif [ $? -eq 2 ]; then
    echo "ERROR FAILED comparing versions of repo internal ontology"
fi

# 2. ------ initializer for NG_Internal
if needUpdate ${NG_INTERNAL} "${ns}versionInfo" $repoInternalPath; then
    echo "WARNING the default initial content of NG_Internal has changed, it may need updating!"
elif [ $? -eq 2 ]; then
    echo "ERROR FAILED comparing versions of repo NG_Internal graph"
fi

# 3. ------ initializer for NG_Query
if needUpdate ${NG_QUERY} "${ns}versionInfo" $queryMacrosPath; then
    echo "WARNING the default initial content of Query Macros (NG_Query) has changed, it may need updating!"
elif [ $? -eq 2 ]; then
    echo "ERROR FAILED comparing versions of repo query macros"
fi

# 4. -------- Check data model ontology: use /model service
dmVers=`curl -s -u "${username}:${password}" -G -H 'Accept: text/plain' \
      "${repoURL}/repository/model" | tail +2`

# NOTE: this magic gets sh to parse the double-quoted strings out of TSV results
eval "dmWord=($dmVers)"
dmLoaded=${dmWord[0]}
dmAvail=${dmWord[1]}

if [ "$dmLoaded" = "$dmAvail" ]; then
    echo "INFO SKIP: ${DM}: graph and file are same version ($dmLoaded)"
elif echo "$dmLoaded" | grep -q 'not loaded' || versionLessThan $dmLoaded $dmAvail; then
    echo "INFO Loading EI Data Model Ontology: $DM"
    status=`curl -s -u "${username}:${password}" -d action=load -d source=jar \
      --write-out '%{http_code}' "${repoURL}/repository/model"`
    if [ "$status" = 200 -o "$status" = 201 ]; then
        echo "INFO Successfully loaded Data Model Ontology, status=$status"
    else
        echo "ERROR Failed loading Data Model Ontology, status=$status"
    fi
else
    echo "WARNING ${DM}: ANOMALY, loaded version ($dmLoaded) is LATER than avilable ($dmAvail)"
fi
