Skip to main content

OpenText to SharePoint Migration: Seamless Document Access Through Link Redirection

This case study highlights our innovative approach to resolving a key challenge faced by a client migrating from OpenText to SharePoint. While we’ve anonymized the client for privacy, the scenario is authentic, and the solution we implemented is tailored to their specific needs.

The Challenge

Our client, undergoing a large-scale migration of millions of records from OpenText Content Server (OTCS) to SharePoint Online (SPO), required a strategy that maintained uninterrupted access to documents throughout the migration, which was projected to span several months. Their goal was to enable departments to migrate incrementally, ensuring ongoing access to documents still residing in OpenText while seamlessly redirecting users to SharePoint for migrated content.

The solution had to handle a high volume of user traffic, approximately 10,000 requests per second, and remain functional post-migration to ensure that legacy document links in emails, systems, and archives remained operational.

Key Requirements:

  • Redirect requests for documents that have already been migrated to SharePoint.
  • Ensure documents still in OpenText are accessible.
  • Maintain OpenText’s functionality throughout the migration.
  • Sustain up to 10,000 requests per second.
  • Ensure the solution remains operational after migration.

Implementation: Crafting a Future-Proof Redirection Solution

We approached this challenge with the Xillio Link Redirector, a dynamic proxy solution designed for high-performance document redirection. Our solution architecture ensured seamless, transparent access to both migrated and non-migrated documents, with a focus on minimizing user disruption and ensuring a high degree of accuracy and speed.

Strategic Overview

At the heart of this implementation is the Xillio Link Redirector, a purpose-built proxy server that handles redirection with business-grade functionality. By rerouting DNS entries to the Link Redirector, we ensured all user traffic passed through it. The system dynamically checked whether a document had been migrated. If it had, users were seamlessly redirected to the document’s new location in SharePoint. If not, the document remained accessible in OpenText.

Solution Design

Our solution was engineered for reliability, deploying across two virtual machines hosted in different co-locations to ensure high availability and redundancy. We utilized a shared database to track document migration data and an InfluxDB to capture usage and performance metrics, creating a system both resilient and efficient.

The following diagram illustrates the system architecture:

Infrastructure Components

The system employed an NGINX reverse proxy, integrating with the Xillio Link Redirector to provide redirection while managing complex network flows and security protocols, such as NTLM. The Link Redirector itself supports multiple databases, allowing for a variety of possible implementation depending on the clients need. As this is such a flexible setup, we will not go into the specifics of the database implementation in this case study. Just be aware that this database is used to store the redirection rules, and the mapping between the old and new URLs.

Network Infrastructure

This diagram below details how the network infrastructure is set up to route the user requests through the Link Redirector. The user requests are first routed via the DNS Server to the Load Balancer. The load balancer then routes the request to one of the two Link Redirector VMs. It initially connects to the NGINX reverse proxy, which determines if the request is dealing with OpenText system requests and authentication, in which case it is proxied back to the OpenText server. If the request is dealing with a document that has been migrated to SharePoint, the request is proxied to the Link Redirector.

Network Infrastructure

To meet the client’s specific needs, we created custom URL-matching rules, enabling the Link Redirector to intelligently distinguish between migrated and non-migrated content. These rules were optimized for performance, ensuring the correct redirection path was selected with minimal latency.

Below is a list of all the rules we have identified and implemented for this client.

Click to expand the rules

Note: the domain names and IP-adresses are fake to protect the client's privacy

[
{
"type":"lookup",
"pattern":"/otcs/cs.exe/Link/:id",
"destination":"https://acme.sharepoint.com/:id(.*)",
"connector":"livelink",
"fallback":"https://10.11.108.81/otcs/cs.exe/Link/:id",
"redirect":true,
"redirectFallback":false,
"preserveQuery":false,
"priority":100,
"preserveHostHeader":true
},
{
"type":"lookup",
"pattern":"/otcs/cs.exe/Open/:id",
"destination":"https://acme.sharepoint.com/:id(.*)",
"connector":"livelink",
"fallback":"https://10.11.108.81/otcs/cs.exe/Open/:id",
"redirect":true,
"redirectFallback":false,
"preserveQuery":false,
"priority":100,
"preserveHostHeader":true
},
{
"type":"lookup",
"pattern":"/otcs/cs.exe/Overview/:id",
"destination":"https://acme.sharepoint.com/:id(.*)",
"connector":"livelink",
"fallback":"https://10.11.108.81/otcs/cs.exe/Overview/:id",
"redirect":true,
"redirectFallback":false,
"preserveQuery":false,
"priority":100,
"preserveHostHeader":true
},
{
"type":"lookup",
"pattern":"/otcs/cs.exe/Properties/:id",
"destination":"https://acme.sharepoint.com/:id(.*)",
"connector":"livelink",
"fallback":"https://10.11.108.81/otcs/cs.exe/Properties/:id",
"redirect":true,
"redirectFallback":false,
"preserveQuery":false,
"priority":100,
"preserveHostHeader":true
},
{
"type":"lookup",
"pattern":"/otcs/cs.exe ??objId=:id",
"destination":"https://acme.sharepoint.com/:id(.*)",
"connector":"livelink",
"fallback":"https://10.11.108.81/otcs/cs.exe",
"redirect":true,
"redirectFallback":false,
"preserveQuery":false,
"priority":10,
"preserveHostHeader":true
},
{
"type":"rewrite",
"pattern":"/:path(.*)",
"destination":"https://10.11.108.81/:path(.*)",
"redirect":true,
"priority":1,
"preserveHostHeader":true
}
]

We prioritized the most specific URL patterns, such as /otcs/cs.exe/Open/:id, by setting their priority to 100. This ensured that complex routing scenarios were handled accurately, otherwise the fallback (/:path(.*)) could have taken priority. Our configuration also included important parameters to control behavior, such as preserving query parameters and host headers, ensuring consistency and reliability across all redirected links. A few of the most important ones are:

  • redirect: - If the rule is matched, and the document is found in the database, either redirect (if true) the user to the new location or proxy (if false) the request to the original CMS.
  • redirectFallback: - If the document is not found in the database, should we redirect the user to the original CMS or proxy the request to the original CMS.
  • preserveQuery: - If true, the original query parameters are preserved and added to the destination URL.
  • preserveHostHeader: - If true, will attempt to mimic the original request and helps with creating the correct links in the returned documents.
info

The preserviceHostHeader is especially important for OpenText, and must be set to true, as it uses the host header to create correct links within the return HTML pages. If not set correctly, the links in the returned HTML pages would likely be returned as IP-addresses. This would result in unwanted behavior and incorrect links, and then would circumvent the Link Redirector.

Nginx Configuration

While the Link Redirector handled the bulk of the document redirection, NGINX played a critical role in managing SSL certificates and NTLM authentication, enabling secure, authenticated access to the OpenText system. NGINX’s flexibility allowed us to deal with these cases, without the need of developing this complex logic ourselves. This creates a nice synergy between the two applications where they complement eachothers strenghts.

When installing NGINX we had to make sure that the configuration was set up correctly to route the requests to the correct destination. Below is the configuration file we used for this client.

Click to expand the Nginx configuration

Note: the domain names and IP-adresses are fake to protect the client's privacy

nginx.conf
worker_processes  1;

events {
worker_connections 1024;
}

http {
map $host $xlr_ip {
# We assume that the link redirector is running on the same host as the Nginx server
# If this is not the case, it is required to replace the following IP with the actual IP of the link redirector
default 127.0.0.1;
}
map $host $internal_service_ip {
# Replace the following IP with the actual IP of the internal service
default 10.11.108.81;
}

# The following block is required to set the maximum file size that can be uploaded
# to the server. The value is set to 5000M, which is 5GB. We advise to set this value
# not too high, as it can be a security risk.
client_max_body_size 5000M;
proxy_max_temp_file_size 0;
proxy_buffering off;

include mime.types;
default_type application/octet-stream;

sendfile on;
keepalive_timeout 65;

server {
listen 80;

# If you are not using SSL, you can remove the following block
listen 443 ssl;
ssl_certificate certs/xlr.cer;
ssl_certificate_key certs/xlr.key;

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# In some-cases it is required to hardcode the Host-header, in that case
# Replate $host with the actual hostname
proxy_set_header Host $host;


# For Opentext Content Server the following locations are required.
# However, the actual locations may vary depending on the configuration.
# By default `/otcs/cs.exe` is the main entry point for the application.
# For LiveLink the main entry point is `/llisapi.dll`. Update the locations
# as per the actual configuration.
location ~* ^/otcs/cs.exe/link {
proxy_pass http://$xlr_ip:8000;
}
location ~* ^/otcs/cs.exe/open {
proxy_pass http://$xlr_ip:8000;
}
location ~* ^/otcs/cs.exe/overview {
proxy_pass http://$xlr_ip:8000;
}
location ~* ^/otcs/cs.exe/properties {
proxy_pass http://$xlr_ip:8000;
}

location /otcs/cs.exe {
proxy_ssl_verify off;
proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;

if ($request_method = POST) {
proxy_pass https://$internal_service_ip;
break;
}

if ($arg_objID) {
proxy_pass http://$xlr_ip:8000;
break;
}
if ($arg_objId) {
proxy_pass http://$xlr_ip:8000;
break;
}
if ($arg_objid) {
proxy_pass http://$xlr_ip:8000;
break;
}
proxy_pass https://$internal_service_ip;
}

location / {
proxy_pass https://$internal_service_ip;
proxy_ssl_verify off;
proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
}
}
}

For this client, we had to deal with HTTPS/TLS certificates. The client had supplied us with a certificate for their domain, which we installed on the NGINX server. We could have installed it on the Link Redirector, but since NGINX was our first point of contact, we decided to install it there. We've uploaded the certificates near the configuration and set the ssl_certificate and ssl_certificate_key values appropriately. Additionally as we were now taking care of the HTTPS/TLS traffic, we disabled checking the certificates when proxying the requests to the original CMS. This was done by setting proxy_ssl_verify to off. This was causing some issues during the testing phase, and there was no risk in disabling it, as the traffic was already encrypted through NGINX.

Another important part of the configuration is the proxy_set_header directive. As explained in the previous section, it is essential that the Host header is set correctly. This is done by setting the Host header to the original host of the request. For this specific client we ended up hardcoding the value to their OpenText domain, but this was only due to a small configurating in their network infrastructure. In most cases it should be enough to set the Host header to $host or $http_host.