5050import com .atomgraph .core .mapper .BadGatewayExceptionMapper ;
5151import com .atomgraph .core .provider .QueryParamProvider ;
5252import com .atomgraph .linkeddatahub .writer .factory .DataManagerFactory ;
53+ import com .atomgraph .server .vocabulary .LDT ;
5354import com .atomgraph .server .mapper .NotFoundExceptionMapper ;
5455import com .atomgraph .core .riot .RDFLanguages ;
5556import com .atomgraph .core .riot .lang .RDFPostReaderFactory ;
@@ -300,6 +301,10 @@ public class Application extends ResourceConfig
300301 private final Properties oidcRefreshTokens ;
301302 private final URI contextDatasetURI ;
302303 private final Dataset contextDataset ;
304+ private final URI frontendProxy ;
305+ private final URI backendProxyAdmin ;
306+ private final URI backendProxyEndUser ;
307+ private Map <String , com .atomgraph .linkeddatahub .model .ServiceContext > serviceContextMap ;
303308
304309 /**
305310 * Constructs system application and configures it using sevlet config.
@@ -358,7 +363,10 @@ public Application(@Context ServletConfig servletConfig) throws URISyntaxExcepti
358363 servletConfig .getServletContext ().getInitParameter (Google .clientID .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (Google .clientID .getURI ()) : null ,
359364 servletConfig .getServletContext ().getInitParameter (Google .clientSecret .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (Google .clientSecret .getURI ()) : null ,
360365 servletConfig .getServletContext ().getInitParameter (ORCID .clientID .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (ORCID .clientID .getURI ()) : null ,
361- servletConfig .getServletContext ().getInitParameter (ORCID .clientSecret .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (ORCID .clientSecret .getURI ()) : null
366+ servletConfig .getServletContext ().getInitParameter (ORCID .clientSecret .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (ORCID .clientSecret .getURI ()) : null ,
367+ servletConfig .getServletContext ().getInitParameter (LDHC .frontendProxy .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (LDHC .frontendProxy .getURI ()) : null ,
368+ servletConfig .getServletContext ().getInitParameter (LDHC .backendProxyAdmin .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (LDHC .backendProxyAdmin .getURI ()) : null ,
369+ servletConfig .getServletContext ().getInitParameter (LDHC .backendProxyEndUser .getURI ()) != null ? servletConfig .getServletContext ().getInitParameter (LDHC .backendProxyEndUser .getURI ()) : null
362370 );
363371 }
364372
@@ -413,6 +421,9 @@ public Application(@Context ServletConfig servletConfig) throws URISyntaxExcepti
413421 * @param googleClientSecret client secret for Google's OAuth
414422 * @param orcidClientID client ID for ORCID's OAuth
415423 * @param orcidClientSecret client secret for ORCID's OAuth
424+ * @param frontendProxyString frontend (Varnish) proxy URI used for cache invalidation BAN requests, or null
425+ * @param backendProxyAdminString backend proxy URI for the admin SPARQL service (endpoint URI rewriting + cache invalidation), or null
426+ * @param backendProxyEndUserString backend proxy URI for the end-user SPARQL service (endpoint URI rewriting + cache invalidation), or null
416427 */
417428 public Application (final ServletConfig servletConfig , final MediaTypes mediaTypes ,
418429 final Integer maxGetRequestSize , final boolean cacheModelLoads , final boolean preemptiveAuth ,
@@ -430,14 +441,18 @@ public Application(final ServletConfig servletConfig, final MediaTypes mediaType
430441 final String notificationAddressString , final String supportedLanguageCodes , final boolean enableWebIDSignUp , final String oidcRefreshTokensPropertiesPath ,
431442 final String mailUser , final String mailPassword , final String smtpHost , final String smtpPort ,
432443 final String googleClientID , final String googleClientSecret ,
433- final String orcidClientID , final String orcidClientSecret )
444+ final String orcidClientID , final String orcidClientSecret ,
445+ final String frontendProxyString , final String backendProxyAdminString , final String backendProxyEndUserString )
434446 {
435447 if (contextDatasetURIString == null )
436448 {
437449 if (log .isErrorEnabled ()) log .error ("Context dataset URI '{}' not configured" , LDHC .contextDataset .getURI ());
438450 throw new ConfigurationException (LDHC .contextDataset );
439451 }
440452 this .contextDatasetURI = URI .create (contextDatasetURIString );
453+ this .frontendProxy = frontendProxyString != null ? URI .create (frontendProxyString ) : null ;
454+ this .backendProxyAdmin = backendProxyAdminString != null ? URI .create (backendProxyAdminString ) : null ;
455+ this .backendProxyEndUser = backendProxyEndUserString != null ? URI .create (backendProxyEndUserString ) : null ;
441456
442457 if (clientKeyStoreURIString == null )
443458 {
@@ -736,12 +751,54 @@ public Application(final ServletConfig servletConfig, final MediaTypes mediaType
736751 BuiltinPersonalities .model .add (com .atomgraph .linkeddatahub .apps .model .Application .class , new com .atomgraph .linkeddatahub .apps .model .impl .ApplicationImplementation ());
737752 BuiltinPersonalities .model .add (com .atomgraph .linkeddatahub .apps .model .Dataset .class , new com .atomgraph .linkeddatahub .apps .model .impl .DatasetImplementation ());
738753 BuiltinPersonalities .model .add (com .atomgraph .linkeddatahub .apps .model .Package .class , new com .atomgraph .linkeddatahub .apps .model .impl .PackageImplementation ());
739- BuiltinPersonalities .model .add (Service .class , new com .atomgraph .linkeddatahub .model .impl .ServiceImplementation (noCertClient , mediaTypes , maxGetRequestSize ));
754+ BuiltinPersonalities .model .add (Service .class , new com .atomgraph .linkeddatahub .model .impl .ServiceImplementation ());
740755 BuiltinPersonalities .model .add (Import .class , ImportImpl .factory );
741756 BuiltinPersonalities .model .add (RDFImport .class , RDFImportImpl .factory );
742757 BuiltinPersonalities .model .add (CSVImport .class , CSVImportImpl .factory );
743758 BuiltinPersonalities .model .add (com .atomgraph .linkeddatahub .model .File .class , FileImpl .factory );
744-
759+
760+ // Build ServiceContext map: keyed by service URI, associates each service with its client and proxy config.
761+ // Admin services get backendProxyAdmin; end-user services get backendProxyEndUser.
762+ serviceContextMap = new HashMap <>();
763+ org .apache .jena .rdf .model .Model ctxUnion = contextDataset .getUnionModel ();
764+ ResIterator serviceIt = ctxUnion .listSubjectsWithProperty (org .apache .jena .vocabulary .RDF .type ,
765+ com .atomgraph .core .vocabulary .SD .Service );
766+ try
767+ {
768+ while (serviceIt .hasNext ())
769+ {
770+ Resource svcResource = serviceIt .next ();
771+ com .atomgraph .linkeddatahub .model .Service svc = svcResource .as (com .atomgraph .linkeddatahub .model .Service .class );
772+ // Determine which proxy applies: check which type of application references this service
773+ org .apache .jena .rdf .model .ResIterator appIt = ctxUnion .listSubjectsWithProperty (
774+ LDT .service , svcResource );
775+ boolean referencedByAdmin = false ;
776+ boolean referencedByEndUser = false ;
777+ try
778+ {
779+ while (appIt .hasNext ())
780+ {
781+ Resource app = appIt .next ();
782+ if (app .hasProperty (org .apache .jena .vocabulary .RDF .type , LAPP .AdminApplication ))
783+ referencedByAdmin = true ;
784+ if (app .hasProperty (org .apache .jena .vocabulary .RDF .type , LAPP .EndUserApplication ))
785+ referencedByEndUser = true ;
786+ }
787+ }
788+ finally
789+ {
790+ appIt .close ();
791+ }
792+ URI proxy = referencedByAdmin ? backendProxyAdmin : (referencedByEndUser ? backendProxyEndUser : null );
793+ serviceContextMap .put (svcResource .getURI (),
794+ new com .atomgraph .linkeddatahub .model .ServiceContext (svc , noCertClient , mediaTypes , maxGetRequestSize , proxy ));
795+ }
796+ }
797+ finally
798+ {
799+ serviceIt .close ();
800+ }
801+
745802 // TO-DO: config property for cacheModelLoads
746803 endUserOntModelSpecs = new HashMap <>();
747804 dataManager = new DataManagerImpl (locationMapper , new HashMap <>(), GraphStoreClient .create (client , mediaTypes ), cacheModelLoads , preemptiveAuth , resolvingUncached );
@@ -1436,12 +1493,12 @@ public Map<Integer, Resource> getLengthMap(Map<URI, Resource> apps)
14361493 */
14371494 public void submitImport (CSVImport csvImport , com .atomgraph .linkeddatahub .apps .model .Application app , Service service , Service adminService , String baseURI , GraphStoreClient gsc )
14381495 {
1439- new ImportExecutor (importThreadPool ).start (service , adminService , baseURI , gsc , csvImport );
1496+ new ImportExecutor (importThreadPool ).start (service , adminService , this , baseURI , gsc , csvImport );
14401497 }
1441-
1498+
14421499 /**
14431500 * Submits RDF import for asynchronous execution.
1444- *
1501+ *
14451502 * @param rdfImport import resource
14461503 * @param app current application
14471504 * @param service current SPARQL service
@@ -1451,7 +1508,7 @@ public void submitImport(CSVImport csvImport, com.atomgraph.linkeddatahub.apps.m
14511508 */
14521509 public void submitImport (RDFImport rdfImport , com .atomgraph .linkeddatahub .apps .model .Application app , Service service , Service adminService , String baseURI , GraphStoreClient gsc )
14531510 {
1454- new ImportExecutor (importThreadPool ).start (service , adminService , baseURI , gsc , rdfImport );
1511+ new ImportExecutor (importThreadPool ).start (service , adminService , this , baseURI , gsc , rdfImport );
14551512 }
14561513
14571514 /**
@@ -1768,15 +1825,38 @@ public Client getExternalClient()
17681825 }
17691826
17701827 /**
1771- * Bans URL from the proxy cache.
1828+ * Returns the service context for the given service (client + proxy configuration).
1829+ * The context is keyed by the service's URI string.
1830+ *
1831+ * @param service SPARQL service
1832+ * @return service context, or {@code null} if the service is not registered
1833+ */
1834+ public com .atomgraph .linkeddatahub .model .ServiceContext getServiceContext (com .atomgraph .linkeddatahub .model .Service service )
1835+ {
1836+ if (service == null ) throw new IllegalArgumentException ("Service cannot be null" );
1837+ return serviceContextMap .get (service .getURI ());
1838+ }
1839+
1840+ /**
1841+ * Returns the frontend proxy URI used for cache invalidation BAN requests.
1842+ *
1843+ * @return frontend proxy URI, or {@code null} if not configured
1844+ */
1845+ public URI getFrontendProxy ()
1846+ {
1847+ return frontendProxy ;
1848+ }
1849+
1850+ /**
1851+ * Bans URL from the proxy cache using the given proxy URI.
17721852 *
1773- * @param proxy proxy server resource
1853+ * @param proxyURI proxy URI
17741854 * @param url banned URL
17751855 * @param urlEncode if true, the banned URL value will be URL-encoded
1776- * @throws IllegalArgumentException if url is null
17771856 */
1778- public void ban (Resource proxy , String url , boolean urlEncode )
1857+ public void ban (URI proxyURI , String url , boolean urlEncode )
17791858 {
1859+ if (proxyURI == null ) throw new IllegalArgumentException ("Proxy URI cannot be null" );
17801860 if (url == null ) throw new IllegalArgumentException ("URL cannot be null" );
17811861
17821862 // Extract path from URL - Varnish req.url only contains the path, not the full URL
@@ -1786,7 +1866,7 @@ public void ban(Resource proxy, String url, boolean urlEncode)
17861866
17871867 final String urlValue = urlEncode ? UriComponent .encode (path , UriComponent .Type .UNRESERVED ) : path ;
17881868
1789- try (Response cr = getClient ().target (proxy . getURI () ).
1869+ try (Response cr = getClient ().target (proxyURI ).
17901870 request ().
17911871 header (CacheInvalidationFilter .HEADER_NAME , urlValue ).
17921872 method ("BAN" , Response .class ))
0 commit comments