Introduction

When using Azure Storage with the IIIF server Cantaloupe, there are cases where the IIIF URL identifier differs from the actual file path on Azure Storage. This article explains in detail how to solve this problem using delegate scripts.

Problem

Suppose you are managing images with the following file structure:

AzureimSatgoeccrsoallglleeiiecttcCteetoimmino0i0iotn0t0tna11e2e2i//m/m/n00e00r12:__00m00y11c..ojjnpptggainer

However, you want to access them via IIIF URLs like the following:

https://example.com/iiif/3/collection1/item001/item001_001.jpg/info.json

In this case, the IIIF URL identifier (collection1/item001/item001_001.jpg) differs from the actual Azure Storage path (images/collection1/item001/item001_001.jpg).

Since AzureStorageSource does not have a PATH_PREFIX setting like S3Source, delegate scripts must be used to solve this problem.

Solution

1. Docker Compose Configuration

sercvainielrctmnoaeeaavCCCCCCCl-b-----sslgiAAAAAAAuet:oerNNNNNNNm"l"""""au:oTTTTTTTe.stttttrpnAAAAAAAs/:rrrrrteimLLLLLLL:daaaaa::seOOOOOOOeeeeeelnUUUUUUUlfffffaatPPPPPPPeiiiiiln:EEEEEEEgkkkkkwd_______a.....aoSAAAADDtehhhhyrOZZZZEEenttttsaUUUUULLsatttt/RRRRREE.bppppcCEEEEGGrl....aESSSSAAberrrsn_TTTTTT:=oooetSOOOOEE/tuuuraTRRRR__ortttvlAAAAASSpueeeioTGGGGCCterrrcuIEEEERR/"sssepCSSSSIIc...se:OOOOPPaccc.:UUUUTTnaaacmARRRR__tnnnaazCCCCEPatttniuEEEENAlaaatnr____ATolllaeAACLBHuooolSCCOOLNpuuuotCCNOEAepppuoOOTKDM/eeeprUUAU:Ed...eaNNIP:eret.gTTN_"lunlle__ESt"eltsoSNKRTr/ger=aoAE_Ruoa=ytduMYNAeptHprbrE:AT"teoouac:ME/ssiele$EGc.tn"a${:Yar(tn{A:nb`scAZ$t:e=eZU{SarxwrURAcloae.REZro"mbsE_Uiupse_SRpplerSTEteecvTO_L/.ueORSodcrrRAToeoe.AGOklm"pGERue`oE_Apg)r_AGSa"tACEtt=CC_re8COCas1OUOt.8UNNer2NTTgb"T_Ay"_KINENAYE#M}RE_I}NmApMoEr}tant

2. Delegate Script (delegates.rb)

Development/Debug Version

First, verify the behavior with a version that includes debug output:

###ce#lnDa#a###de###dede###dededededest#en#enen#enenenenlsAtCfiierdAfptdfptdOf{df{df[dfndecrodfneuururt}}]igCc_naebpprdttptuatuheermlaueavzniluueuhrseuseexxeetssceutdotttreetrttdtetscrriebssunn_"h"rraaooetefn_rtaDoDraacdsmrssitk""nniuErEe__tacDsIteieDDictBiBqiiitrefoIorfyEEblahUzUuiioailorIriBBltoGeGiiin(perFa=e=UUoir:(:rffsotg:grGGboioe23(pascUec"::_nzppad__otfttoRsoikertuiipioeonLonmIOem(eitmnntorrtutanuyeo_oheffinieiregpttpanotooosAnxdcxeuphtusrhrrnzgteetstuoitiomms=un_[/tdoh=zdaarctb'#isnoestt={eoili{dbsr{ii}Snfodieli}coo{)ttibedno=z)ann}oee_netbel__)rxrktni{lrrateitfk}ceeegtyfiie)adsseoifeyl"ppSeir:loooAre:ennuz'r#dssru]}#{"eecr"{b__eeiilkkfdoeewSebyyitcn_sstootk((hrnieooatfyppSgei}ttcexe"iirtrooib}nnpl"sstoLb==ook{{ke}}uy))pStrategy

Clean Production Version

After verifying the behavior, remove debug output for production:

###ce#lnCa#a####de###dede###dede###de###dedost#en#enen#enen#en#enmsAtMCfirdAftdftdAf{df{dRf[dAfndpcraodeurrd}}e]dilCc_inaettpuaudeedrdmleueanvznuhreueixxaeietssceutreettttcdttetscfrrinn_hirrtaiaooeuteftaooaaicoddmrsnsiiiurn__otnaeDscItedctiaiiniatlefotIoreahzliiolaeloriIrntoeiifn(geroFa=tir(Iffesmoag:ngioioI23a(eptascUecfnzpI__tottettocRsoietFiiupaieonaLonem(innrtdosrtlutreooiffeiancieliretpnnoootsrnxedcx?htsfrr(naigtdeetoiommes=pn_["do=raam(tcbtb'isnmttp=r{oyilims{aiite}fnfoda-}tooy{t)otCibeg=)inn}ureae_neao__l)rxnrktsl{nrrinAttei/l}eeszatyf#o)rsstnuloi{weppiroeisoowleuArdapnnhSpz'elossewteu]nlneenhorts__ereiirekknnaffeeeogSiqkyytnetceuessoSoorey((utorn}ssoosuat"tppeurges(ttdscex:eii)eetmoodbnpnn)wlitssiolytb==hwkh{{See}}cyn))rinpottLouoskeudp)Strategy

Troubleshooting

Common Errors and Solutions

1. Cannot invoke "edu.illinois.library.cantaloupe.delegate.DelegateProxy.getAzureStorageSourceBlobKey()" because the return value is null

Cause: Delegate script is not loaded correctly or has a syntax error

Solution:

  • Check the delegate script syntax: ruby -c delegates.rb
  • Verify that attr_accessor :context is defined
  • Check the container logs for delegate script loading status

2. undefined method 'context=' for CustomDelegate

Cause: attr_accessor :context is not defined

Solution: Add attr_accessor :context at the beginning of the class

3. undefined method 'pre_authorize' etc.

Cause: Required delegate methods are not defined

Solution: Add the necessary methods, referring to the complete delegate script above

4. class org.jruby.RubyHash cannot be cast to class java.lang.String

Cause: metadata() method is returning an incorrect type

Solution:

  • Return nil when metadata is not used
  • It is important to return nil rather than an empty hash {}
deenfndimleta#daRteat(uorpntinoinls,=no{t})emptyhash{}

Debugging Methods

  1. Log monitoring:
dockerlogscontainer-name-f
  1. Syntax check:
ruby-cdelegates.rb
  1. Incremental testing: Start with a simple delegate script and gradually add features

Verification

1. Start the Service

dockercomposeup-d

2. Test Access

#c#cuuFrFrelelttchchhthtttipipnsmsf:a:og/./e/jeesxxoaanmmppllee..ccoomm//iiiiiiff//33//ccoolllleeccttiioonn11//iitteemm000011//iitteemm000011__000011..jjppgg//ifnuflol./j3s0o0n,/0/default.jpg

3. Check Logs (When Using Debug Version)

Expected log output:

https://example.com/iiif/3/collection1/item001/item001_001.jpg/info.json

0

Summary

By using Cantaloupe’s delegate script, you can absorb the difference between IIIF URLs and actual storage paths. Key points:

  1. ScriptLookupStrategy setting: Required for using delegate scripts with AzureStorageSource
  2. attr_accessor :context: Required for Cantaloupe to set context information
  3. azurestoragesource_blob_key method: Main logic for path conversion
  4. Other delegate methods: Minimally required to avoid errors

The method introduced in this article enables flexible file management and IIIF URL design. It is particularly effective when you want to add IIIF support without changing existing file structures.