Merge branch 'master' into KNOX-998-Package_Restructuring # Conflicts: # gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/beans/BeanConverter.java # gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java
Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/2c69152f Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/2c69152f Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/2c69152f Branch: refs/heads/master Commit: 2c69152f49a22dbd6c9947a26965e3041a4f92d9 Parents: 1451428 d4b0dc6 Author: Sandeep More <[email protected]> Authored: Mon Nov 13 09:44:22 2017 -0500 Committer: Sandeep More <[email protected]> Committed: Mon Nov 13 09:44:22 2017 -0500 ---------------------------------------------------------------------- CHANGES | 85 +++ build.xml | 2 +- gateway-applications/pom.xml | 2 +- gateway-demo-ldap-launcher/pom.xml | 2 +- gateway-demo-ldap/pom.xml | 2 +- gateway-discovery-ambari/pom.xml | 2 +- gateway-i18n-logging-log4j/pom.xml | 2 +- gateway-i18n-logging-sl4j/pom.xml | 2 +- gateway-i18n/pom.xml | 2 +- gateway-provider-ha/pom.xml | 2 +- .../ha/provider/impl/DefaultURLManagerTest.java | 19 + .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- gateway-provider-jersey/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- gateway-provider-rewrite/pom.xml | 2 +- gateway-provider-security-authc-anon/pom.xml | 2 +- gateway-provider-security-authz-acls/pom.xml | 2 +- gateway-provider-security-hadoopauth/pom.xml | 2 +- gateway-provider-security-jwt/pom.xml | 2 +- gateway-provider-security-pac4j/pom.xml | 2 +- gateway-provider-security-preauth/pom.xml | 2 +- gateway-provider-security-shiro/pom.xml | 2 +- gateway-provider-security-webappsec/pom.xml | 2 +- gateway-release/pom.xml | 6 +- gateway-server-launcher/pom.xml | 2 +- gateway-server-xforwarded-filter/pom.xml | 2 +- gateway-server/pom.xml | 2 +- .../knox/gateway/deploy/DeploymentFactory.java | 23 + .../ServiceDefinitionDeploymentContributor.java | 14 +- .../instr/InstrHttpClientBuilderProvider.java | 3 +- .../builder/BeanPropertyTopologyBuilder.java | 11 + .../topology/simple/SimpleDescriptor.java | 10 + .../simple/SimpleDescriptorHandler.java | 51 +- .../topology/simple/SimpleDescriptorImpl.java | 42 +- .../xml/KnoxFormatXmlTopologyRules.java | 2 + .../src/main/resources/conf/topology-v1.xsd | 1 + .../simple/SimpleDescriptorFactoryTest.java | 631 +++++++++++++------ .../simple/SimpleDescriptorHandlerTest.java | 8 + gateway-service-admin/pom.xml | 4 +- .../service/admin/TopologiesResource.java | 25 +- .../service/admin/beans/BeanConverter.java | 2 + .../gateway/service/admin/beans/Topology.java | 11 + gateway-service-as/pom.xml | 2 +- gateway-service-definitions/pom.xml | 2 +- .../service/definition/CustomDispatch.java | 11 + .../resources/services/livy/0.4.0/rewrite.xml | 33 + .../resources/services/livy/0.4.0/service.xml | 28 + .../resources/services/nifi/1.4.0/rewrite.xml | 27 + .../resources/services/nifi/1.4.0/service.xml | 30 + .../services/oozieui/4.2.0/rewrite.xml | 27 +- gateway-service-hbase/pom.xml | 2 +- gateway-service-health/pom.xml | 4 +- gateway-service-hive/pom.xml | 2 +- gateway-service-knoxsso/pom.xml | 4 +- gateway-service-knoxssout/pom.xml | 4 +- gateway-service-knoxtoken/pom.xml | 4 +- gateway-service-nifi/pom.xml | 38 ++ .../hadoop/gateway/dispatch/NiFiDispatch.java | 106 ++++ .../hadoop/gateway/dispatch/NiFiHaDispatch.java | 111 ++++ .../hadoop/gateway/dispatch/NiFiHeaders.java | 26 + .../gateway/dispatch/NiFiRequestUtil.java | 89 +++ .../gateway/dispatch/NiFiResponseUtil.java | 89 +++ gateway-service-rm/pom.xml | 2 +- gateway-service-storm/pom.xml | 2 +- gateway-service-test/pom.xml | 4 +- gateway-service-tgs/pom.xml | 2 +- gateway-service-vault/pom.xml | 4 +- gateway-service-webhdfs/pom.xml | 2 +- gateway-shell-launcher/pom.xml | 2 +- gateway-shell-release/pom.xml | 2 +- gateway-shell-samples/pom.xml | 2 +- gateway-shell/pom.xml | 2 +- gateway-spi/pom.xml | 6 +- .../dispatch/DefaultHttpClientFactory.java | 55 +- .../knox/gateway/i18n/GatewaySpiMessages.java | 3 + .../apache/knox/gateway/topology/Topology.java | 9 + gateway-test-release-utils/pom.xml | 2 +- gateway-test-release/pom.xml | 2 +- gateway-test-release/webhdfs-kerb-test/pom.xml | 4 +- gateway-test-release/webhdfs-test/pom.xml | 4 +- gateway-test-utils/pom.xml | 2 +- gateway-test/pom.xml | 2 +- .../deploy/DeploymentFactoryFuncTest.java | 5 + gateway-util-common/pom.xml | 2 +- gateway-util-configinjector/pom.xml | 2 +- gateway-util-launcher/pom.xml | 2 +- gateway-util-urltemplate/pom.xml | 2 +- hadoop-examples/pom.xml | 2 +- knox-cli-launcher/pom.xml | 2 +- pom.xml | 10 +- 99 files changed, 1504 insertions(+), 289 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-demo-ldap-launcher/pom.xml ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-demo-ldap/pom.xml ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-provider-ha/src/test/java/org/apache/knox/gateway/ha/provider/impl/DefaultURLManagerTest.java ---------------------------------------------------------------------- diff --cc gateway-provider-ha/src/test/java/org/apache/knox/gateway/ha/provider/impl/DefaultURLManagerTest.java index c8b6c58,0000000..a2cfa54 mode 100644,000000..100644 --- a/gateway-provider-ha/src/test/java/org/apache/knox/gateway/ha/provider/impl/DefaultURLManagerTest.java +++ b/gateway-provider-ha/src/test/java/org/apache/knox/gateway/ha/provider/impl/DefaultURLManagerTest.java @@@ -1,73 -1,0 +1,92 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.ha.provider.impl; + +import org.junit.Test; + +import java.util.ArrayList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DefaultURLManagerTest { + + @Test + public void testActiveURLManagement() { + ArrayList<String> urls = new ArrayList<>(); + String url1 = "http://host1"; + urls.add(url1); + String url2 = "http://host2"; + urls.add(url2); + DefaultURLManager manager = new DefaultURLManager(); + manager.setURLs(urls); + assertTrue(manager.getURLs().containsAll(urls)); + assertEquals(url1, manager.getActiveURL()); + manager.markFailed(url1); + assertEquals(url2, manager.getActiveURL()); + manager.markFailed(url2); + assertEquals(url1, manager.getActiveURL()); + } + ++ /** ++ * KNOX-1104 ++ * Verify that a service with HaProvider configuration, but only a single URL does not break the HaProvider. ++ */ ++ @Test ++ public void testSingleURLManagement() { ++ ArrayList<String> urls = new ArrayList<>(); ++ String url1 = "http://host1"; ++ urls.add(url1); ++ DefaultURLManager manager = new DefaultURLManager(); ++ manager.setURLs(urls); ++ assertTrue(manager.getURLs().containsAll(urls)); ++ assertEquals(url1, manager.getActiveURL()); ++ manager.markFailed(url1); ++ assertEquals(url1, manager.getActiveURL()); ++ manager.markFailed(url1); ++ assertEquals(url1, manager.getActiveURL()); ++ } ++ + @Test + public void testMarkingFailedURL() { + ArrayList<String> urls = new ArrayList<>(); + String url1 = "http://host1:4555"; + urls.add(url1); + String url2 = "http://host2:1234"; + urls.add(url2); + String url3 = "http://host1:1234"; + urls.add(url3); + String url4 = "http://host2:4555"; + urls.add(url4); + DefaultURLManager manager = new DefaultURLManager(); + manager.setURLs(urls); + assertTrue(manager.getURLs().containsAll(urls)); + assertEquals(url1, manager.getActiveURL()); + manager.markFailed(url1); + assertEquals(url2, manager.getActiveURL()); + manager.markFailed(url1); + assertEquals(url2, manager.getActiveURL()); + manager.markFailed(url3); + assertEquals(url2, manager.getActiveURL()); + manager.markFailed(url4); + assertEquals(url2, manager.getActiveURL()); + manager.markFailed(url2); + assertEquals(url3, manager.getActiveURL()); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-release/pom.xml ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server-launcher/pom.xml ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/main/java/org/apache/knox/gateway/deploy/DeploymentFactory.java ---------------------------------------------------------------------- diff --cc gateway-server/src/main/java/org/apache/knox/gateway/deploy/DeploymentFactory.java index bb8f1f2,0000000..b3eabb2 mode 100644,000000..100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/deploy/DeploymentFactory.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/deploy/DeploymentFactory.java @@@ -1,772 -1,0 +1,795 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.deploy; + +import java.beans.Statement; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.TreeMap; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +import org.apache.knox.gateway.GatewayMessages; +import org.apache.knox.gateway.GatewayServlet; +import org.apache.knox.gateway.config.GatewayConfig; +import org.apache.knox.gateway.deploy.impl.ApplicationDeploymentContributor; +import org.apache.knox.gateway.descriptor.GatewayDescriptor; +import org.apache.knox.gateway.descriptor.GatewayDescriptorFactory; +import org.apache.knox.gateway.i18n.messages.MessagesFactory; +import org.apache.knox.gateway.services.GatewayServices; +import org.apache.knox.gateway.services.registry.ServiceRegistry; +import org.apache.knox.gateway.topology.Application; +import org.apache.knox.gateway.topology.Provider; +import org.apache.knox.gateway.topology.Service; +import org.apache.knox.gateway.topology.Topology; +import org.apache.knox.gateway.topology.Version; +import org.apache.knox.gateway.util.ServiceDefinitionsLoader; +import org.apache.knox.gateway.util.Urls; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.Asset; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.EnterpriseArchive; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.descriptor.api.Descriptors; +import org.jboss.shrinkwrap.descriptor.api.webapp30.WebAppDescriptor; +import org.jboss.shrinkwrap.descriptor.api.webcommon30.FilterType; +import org.jboss.shrinkwrap.descriptor.api.webcommon30.ServletType; + +public abstract class DeploymentFactory { + + private static final String SERVLET_NAME_SUFFIX = "-knox-gateway-servlet"; + private static final String FILTER_NAME_SUFFIX = "-knox-gateway-filter"; + private static final GatewayMessages log = MessagesFactory.get( GatewayMessages.class ); + private static GatewayServices gatewayServices = null; + + private static Map<String,Map<String,Map<Version, ServiceDeploymentContributor>>> SERVICE_CONTRIBUTOR_MAP; + static { + loadServiceContributors(); + } + + private static Set<ProviderDeploymentContributor> PROVIDER_CONTRIBUTORS; + private static Map<String,Map<String,ProviderDeploymentContributor>> PROVIDER_CONTRIBUTOR_MAP; + static { + loadProviderContributors(); + } + + public static void setGatewayServices(GatewayServices services) { + DeploymentFactory.gatewayServices = services; + } + + static List<Application> findApplicationsByUrl( Topology topology, String url ) { + List<Application> foundApps = new ArrayList<Application>(); + if( topology != null ) { + url = Urls.trimLeadingAndTrailingSlash( url ); + Collection<Application> searchApps = topology.getApplications(); + if( searchApps != null ) { + for( Application searchApp : searchApps ) { + List<String> searchUrls = searchApp.getUrls(); + if( searchUrls == null || searchUrls.isEmpty() ) { + searchUrls = new ArrayList<String>(1); + searchUrls.add( searchApp.getName() ); + } + for( String searchUrl : searchUrls ) { + if( url.equalsIgnoreCase( Urls.trimLeadingAndTrailingSlash( searchUrl ) ) ) { + foundApps.add( searchApp ); + break; + } + } + } + } + } + return foundApps; + } + + // Verify that there are no two apps with duplicate urls. + static void validateNoAppsWithDuplicateUrlsInTopology( Topology topology ) { + if( topology != null ) { + Collection<Application> apps = topology.getApplications(); + if( apps != null ) { + for( Application app : apps ) { + List<String> urls = app.getUrls(); + if( urls == null || urls.isEmpty() ) { + urls = new ArrayList<String>(1); + urls.add( app.getName() ); + } + for( String url : urls ) { + List<Application> dups = findApplicationsByUrl( topology, url ); + if( dups != null ) { + for( Application dup : dups ) { + if( dup != app ) { + throw new DeploymentException( "Topology " + topology.getName() + " contains applications " + app.getName() + " and " + dup.getName() + " with the same url: " + url ); + } + } + } + } + } + } + } + } + + // Verify that if there are services that there are no applications with a root url. + static void validateNoAppsWithRootUrlsInServicesTopology( Topology topology ) { + if( topology != null ) { + if( topology.getServices() != null && !topology.getServices().isEmpty() ) { + List<Application> dups = findApplicationsByUrl( topology, "/" ); + if( dups != null && !dups.isEmpty() ) { + throw new DeploymentException( "Topology " + topology.getName() + " contains both services and an application " + dups.get( 0 ).getName() + " with a root url." ); + } + } + } + } + + static void validateTopology( Topology topology ) { + validateNoAppsWithRootUrlsInServicesTopology( topology ); + validateNoAppsWithDuplicateUrlsInTopology( topology ); + } + + public static EnterpriseArchive createDeployment( GatewayConfig config, Topology topology ) { + validateTopology( topology ); + loadStacksServiceContributors( config ); + Map<String,List<ProviderDeploymentContributor>> providers = selectContextProviders( topology ); + Map<String,List<ServiceDeploymentContributor>> services = selectContextServices( topology ); + Map<String,ServiceDeploymentContributor> applications = selectContextApplications( config, topology ); + EnterpriseArchive ear = ShrinkWrap.create( EnterpriseArchive.class, topology.getName() ); + ear.addAsResource( toStringAsset( topology ), "topology.xml" ); + if( !services.isEmpty() ) { + WebArchive war = createServicesDeployment( config, topology, providers, services ); + ear.addAsModule( war ); + } + if( !applications.isEmpty() ) { + for( Map.Entry<String, ServiceDeploymentContributor> application : applications.entrySet() ) { + WebArchive war = createApplicationDeployment( config, topology, providers, application ); + ear.addAsModule( war ); + } + } + return ear; + } + + private static WebArchive createServicesDeployment( + GatewayConfig config, + Topology topology, + Map<String,List<ProviderDeploymentContributor>> providers, + Map<String,List<ServiceDeploymentContributor>> services ) { + DeploymentContext context = createDeploymentContext( config, "/", topology, providers ); + initialize( context, providers, services, null ); + contribute( context, providers, services, null ); + finalize( context, providers, services, null ); + return context.getWebArchive(); + } + + public static WebArchive createApplicationDeployment( + GatewayConfig config, + Topology topology, + Map<String,List<ProviderDeploymentContributor>> providers, + Map.Entry<String,ServiceDeploymentContributor> application ) { + String appPath = "/" + Urls.trimLeadingAndTrailingSlash( application.getKey() ); + DeploymentContext context = createDeploymentContext( config, appPath, topology, providers ); + initialize( context, providers, null, application ); + contribute( context, providers, null, application ); + finalize( context, providers, null, application ); + return context.getWebArchive(); + } + + private static Asset toStringAsset( Topology topology ) { + StringWriter writer = new StringWriter(); + String xml; + try { + Map<String,Object> properties = new HashMap<>(2); + properties.put( "eclipselink-oxm-xml", + "org/apache/knox/gateway/topology/topology_binding-xml.xml"); + properties.put( "eclipselink.media-type", "application/xml" ); + JAXBContext jaxbContext = JAXBContext.newInstance( Topology.class.getPackage().getName(), Topology.class.getClassLoader() , properties ); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true ); + marshaller.marshal( topology, writer ); + writer.close(); + xml = writer.toString(); + } catch (IOException e) { + throw new DeploymentException( "Failed to marshall topology.", e ); + } catch (JAXBException e) { + throw new DeploymentException( "Failed to marshall topology.", e ); + } + StringAsset asset = new StringAsset( xml ); + return asset; + } + + private static DeploymentContext createDeploymentContext( + GatewayConfig config, + String archivePath, + Topology topology, + Map<String,List<ProviderDeploymentContributor>> providers ) { + archivePath = Urls.encode( archivePath ); + WebArchive webArchive = ShrinkWrap.create( WebArchive.class, archivePath ); + WebAppDescriptor webAppDesc = Descriptors.create( WebAppDescriptor.class ); + GatewayDescriptor gateway = GatewayDescriptorFactory.create(); + DeploymentContext context = new DeploymentContextImpl( + config, topology, gateway, webArchive, webAppDesc, providers ); + return context; + } + + // Scan through the providers in the topology. Collect any named providers in their roles list. + // Scan through all of the loaded providers. For each that doesn't have an existing provider in the role + // list add it. + private static Map<String,List<ProviderDeploymentContributor>> selectContextProviders( Topology topology ) { + Map<String,List<ProviderDeploymentContributor>> providers = new LinkedHashMap<String, List<ProviderDeploymentContributor>>(); ++ addMissingDefaultProviders(topology); + collectTopologyProviders( topology, providers ); + collectDefaultProviders( providers ); + return providers; + } + ++ private static void addMissingDefaultProviders(Topology topology) { ++ Collection<Provider> providers = topology.getProviders(); ++ HashMap<String, String> providerMap = new HashMap<>(); ++ for (Provider provider : providers) { ++ providerMap.put(provider.getRole(), provider.getName()); ++ } ++ // first make sure that the required provider is available from the serviceloaders ++ // for some tests the number of providers are limited to the classpath of the module ++ // and exceptions will be thrown as topologies are deployed even though they will ++ // work fine at actual server runtime. ++ if (PROVIDER_CONTRIBUTOR_MAP.get("identity-assertion") != null) { ++ // check for required providers and add the defaults if missing ++ if (!providerMap.containsKey("identity-assertion")) { ++ Provider idassertion = new Provider(); ++ idassertion.setRole("identity-assertion"); ++ idassertion.setName("Default"); ++ idassertion.setEnabled(true); ++ providers.add(idassertion); ++ } ++ } ++ } ++ + private static void collectTopologyProviders( + Topology topology, Map<String, List<ProviderDeploymentContributor>> defaults ) { + for( Provider provider : topology.getProviders() ) { + String name = provider.getName(); + if( name != null ) { + String role = provider.getRole(); + Map<String,ProviderDeploymentContributor> nameMap = PROVIDER_CONTRIBUTOR_MAP.get( role ); + if( nameMap != null ) { + ProviderDeploymentContributor contributor = nameMap.get( name ); + // If there isn't a contributor with this role/name try to find a "*" contributor. + if( contributor == null ) { + nameMap = PROVIDER_CONTRIBUTOR_MAP.get( "*" ); + if( nameMap != null ) { + contributor = nameMap.get( name ); + } + } + if( contributor != null ) { + List list = defaults.get( role ); + if( list == null ) { + list = new ArrayList( 1 ); + defaults.put( role, list ); + } + if( !list.contains( contributor ) ) { + list.add( contributor ); + } + } + } + } + } + } + + private static void collectDefaultProviders( Map<String,List<ProviderDeploymentContributor>> defaults ) { + for( ProviderDeploymentContributor contributor : PROVIDER_CONTRIBUTORS ) { + String role = contributor.getRole(); + List<ProviderDeploymentContributor> list = defaults.get( role ); + if( list == null ) { + list = new ArrayList<ProviderDeploymentContributor>(); + defaults.put( role, list ); + } + if( list.isEmpty() ) { + list.add( contributor ); + } + } + } + + // Scan through the services in the topology. + // For each that we find add it to the list of service roles included in the topology. + private static Map<String,List<ServiceDeploymentContributor>> selectContextServices( Topology topology ) { + Map<String,List<ServiceDeploymentContributor>> defaults + = new HashMap<>(); + for( Service service : topology.getServices() ) { + String role = service.getRole(); + ServiceDeploymentContributor contributor = getServiceContributor( role, service.getName(), service.getVersion() ); + if( contributor != null ) { + List<ServiceDeploymentContributor> list = defaults.get( role ); + if( list == null ) { + list = new ArrayList<ServiceDeploymentContributor>( 1 ); + defaults.put( role, list ); + } + if( !list.contains( contributor ) ) { + list.add( contributor ); + } + } + } + return defaults; + } + + private static Map<String,ServiceDeploymentContributor> selectContextApplications( + GatewayConfig config, Topology topology ) { + Map<String,ServiceDeploymentContributor> contributors = new HashMap<>(); + if( topology != null ) { + for( Application application : topology.getApplications() ) { + String name = application.getName(); + if( name == null || name.isEmpty() ) { + throw new DeploymentException( "Topologies cannot contain an application without a name." ); + } + ApplicationDeploymentContributor contributor = new ApplicationDeploymentContributor( config, application ); + List<String> urls = application.getUrls(); + if( urls == null || urls.isEmpty() ) { + urls = new ArrayList<String>( 1 ); + urls.add( "/" + name ); + } + for( String url : urls ) { + if( url == null || url.isEmpty() || url.equals( "/" ) ) { + if( !topology.getServices().isEmpty() ) { + throw new DeploymentException( String.format( + "Topologies with services cannot contain an application (%s) with a root url.", name ) ); + } + } + contributors.put( url, contributor ); + } + } + } + return contributors; + } + + private static void initialize( + DeploymentContext context, + Map<String,List<ProviderDeploymentContributor>> providers, + Map<String,List<ServiceDeploymentContributor>> services, + Map.Entry<String,ServiceDeploymentContributor> applications ) { + WebAppDescriptor wad = context.getWebAppDescriptor(); + String topoName = context.getTopology().getName(); + if( applications == null ) { + String servletName = topoName + SERVLET_NAME_SUFFIX; + wad.createServlet().servletName( servletName ).servletClass( GatewayServlet.class.getName() ); + wad.createServletMapping().servletName( servletName ).urlPattern( "/*" ); + } else { + String filterName = topoName + FILTER_NAME_SUFFIX; + wad.createFilter().filterName( filterName ).filterClass( GatewayServlet.class.getName() ); + wad.createFilterMapping().filterName( filterName ).urlPattern( "/*" ); + } + if (gatewayServices != null) { + gatewayServices.initializeContribution(context); + } else { + log.gatewayServicesNotInitialized(); + } + initializeProviders( context, providers ); + initializeServices( context, services ); + initializeApplications( context, applications ); + } + + private static void initializeProviders( + DeploymentContext context, + Map<String,List<ProviderDeploymentContributor>> providers ) { + if( providers != null ) { + for( Entry<String, List<ProviderDeploymentContributor>> entry : providers.entrySet() ) { + for( ProviderDeploymentContributor contributor : entry.getValue() ) { + try { + injectServices( contributor ); + log.initializeProvider( contributor.getName(), contributor.getRole() ); + contributor.initializeContribution( context ); + } catch( Exception e ) { + log.failedToInitializeContribution( e ); + throw new DeploymentException( "Failed to initialize contribution.", e ); + } + } + } + } + } + + private static void initializeServices( DeploymentContext context, Map<String, List<ServiceDeploymentContributor>> services ) { + if( services != null ) { + for( Entry<String, List<ServiceDeploymentContributor>> entry : services.entrySet() ) { + for( ServiceDeploymentContributor contributor : entry.getValue() ) { + try { + injectServices( contributor ); + log.initializeService( contributor.getName(), contributor.getRole() ); + contributor.initializeContribution( context ); + } catch( Exception e ) { + log.failedToInitializeContribution( e ); + throw new DeploymentException( "Failed to initialize contribution.", e ); + } + } + } + } + } + + private static void initializeApplications( DeploymentContext context, Map.Entry<String, ServiceDeploymentContributor> application ) { + if( application != null ) { + ServiceDeploymentContributor contributor = application.getValue(); + if( contributor != null ) { + try { + injectServices( contributor ); + log.initializeApplication( contributor.getName() ); + contributor.initializeContribution( context ); + } catch( Exception e ) { + log.failedToInitializeContribution( e ); + throw new DeploymentException( "Failed to initialize application contribution.", e ); + } + } + } + } + + private static void injectServices(Object contributor) { + if (gatewayServices != null) { + Statement stmt = null; + for(String serviceName : gatewayServices.getServiceNames()) { + + try { + // TODO: this is just a temporary injection solution + // TODO: test for the existence of the setter before attempting it + // TODO: avoid exception throwing when there is no setter + stmt = new Statement(contributor, "set" + serviceName, new Object[]{gatewayServices.getService(serviceName)}); + stmt.execute(); + } catch (NoSuchMethodException e) { + // TODO: eliminate the possibility of this being thrown up front + } catch (Exception e) { + // Maybe it makes sense to throw exception + log.failedToInjectService( serviceName, e ); + throw new DeploymentException("Failed to inject service.", e); + } + } + } + } + + private static void contribute( + DeploymentContext context, + Map<String,List<ProviderDeploymentContributor>> providers, + Map<String,List<ServiceDeploymentContributor>> services, + Map.Entry<String,ServiceDeploymentContributor> applications ) { + Topology topology = context.getTopology(); + contributeProviders( context, topology, providers ); + contributeServices( context, topology, services ); + contributeApplications( context, topology, applications ); + } + + private static void contributeProviders( DeploymentContext context, Topology topology, Map<String, List<ProviderDeploymentContributor>> providers ) { + for( Provider provider : topology.getProviders() ) { + ProviderDeploymentContributor contributor = getProviderContributor( providers, provider.getRole(), provider.getName() ); + if( contributor != null && provider.isEnabled() ) { + try { + log.contributeProvider( provider.getName(), provider.getRole() ); + contributor.contributeProvider( context, provider ); + } catch( Exception e ) { + // Maybe it makes sense to throw exception + log.failedToContributeProvider( provider.getName(), provider.getRole(), e ); + throw new DeploymentException("Failed to contribute provider.", e); + } + } + } + } + + private static void contributeServices( DeploymentContext context, Topology topology, Map<String, List<ServiceDeploymentContributor>> services ) { + if( services != null ) { + for( Service service : topology.getServices() ) { + ServiceDeploymentContributor contributor = getServiceContributor( service.getRole(), service.getName(), service.getVersion() ); + if( contributor != null ) { + try { + log.contributeService( service.getName(), service.getRole() ); + contributor.contributeService( context, service ); + if( gatewayServices != null ) { + ServiceRegistry sr = gatewayServices.getService( GatewayServices.SERVICE_REGISTRY_SERVICE ); + if( sr != null ) { + String regCode = sr.getRegistrationCode( topology.getName() ); + sr.registerService( regCode, topology.getName(), service.getRole(), service.getUrls() ); + } + } + } catch( Exception e ) { + // Maybe it makes sense to throw exception + log.failedToContributeService( service.getName(), service.getRole(), e ); + throw new DeploymentException( "Failed to contribute service.", e ); + } + } + } + } + } + + private static void contributeApplications( DeploymentContext context, Topology topology, Map.Entry<String, ServiceDeploymentContributor> applications ) { + if( applications != null ) { + ServiceDeploymentContributor contributor = applications.getValue(); + if( contributor != null ) { + try { + log.contributeApplication( contributor.getName() ); + Application applicationDesc = topology.getApplication( applications.getKey() ); + contributor.contributeService( context, applicationDesc ); + } catch( Exception e ) { + log.failedToInitializeContribution( e ); + throw new DeploymentException( "Failed to contribution application.", e ); + } + } + } + } + + public static ProviderDeploymentContributor getProviderContributor( String role, String name ) { + ProviderDeploymentContributor contributor = null; + Map<String,ProviderDeploymentContributor> nameMap = PROVIDER_CONTRIBUTOR_MAP.get( role ); + if( nameMap != null ) { + if( name != null ) { + contributor = nameMap.get( name ); + } else if ( !nameMap.isEmpty() ) { + contributor = nameMap.values().iterator().next(); + } + } + return contributor; + } + + public static ServiceDeploymentContributor getServiceContributor( String role, String name, Version version ) { + ServiceDeploymentContributor contributor = null; + Map<String,Map<Version, ServiceDeploymentContributor>> nameMap = SERVICE_CONTRIBUTOR_MAP.get( role ); + if( nameMap != null && !nameMap.isEmpty()) { + Map<Version, ServiceDeploymentContributor> versionMap = null; + if ( name == null ) { + versionMap = nameMap.values().iterator().next(); + } else { + versionMap = nameMap.get( name ); + } + if ( versionMap != null && !versionMap.isEmpty()) { + if( version == null ) { + contributor = ((TreeMap<Version, ServiceDeploymentContributor>) versionMap).firstEntry().getValue(); + } else { + contributor = versionMap.get( version ); + } + } + } + return contributor; + } + + private static void finalize( + DeploymentContext context, + Map<String,List<ProviderDeploymentContributor>> providers, + Map<String,List<ServiceDeploymentContributor>> services, + Map.Entry<String,ServiceDeploymentContributor> application ) { + try { + // Write the gateway descriptor (gateway.xml) into the war. + StringWriter writer = new StringWriter(); + GatewayDescriptorFactory.store( context.getGatewayDescriptor(), "xml", writer ); + context.getWebArchive().addAsWebInfResource( + new StringAsset( writer.toString() ), + GatewayServlet.GATEWAY_DESCRIPTOR_LOCATION_DEFAULT ); + + // Set the location of the gateway descriptor as a servlet init param. + if( application == null ) { + String servletName = context.getTopology().getName() + SERVLET_NAME_SUFFIX; + ServletType<WebAppDescriptor> servlet = findServlet( context, servletName ); + // Coverity CID 1352314 + if( servlet == null ) { + throw new DeploymentException( "Missing servlet " + servletName ); + } else { + servlet.createInitParam() + .paramName( GatewayServlet.GATEWAY_DESCRIPTOR_LOCATION_PARAM ) + .paramValue( "/WEB-INF/" + GatewayServlet.GATEWAY_DESCRIPTOR_LOCATION_DEFAULT ); + } + } else { + String servletName = context.getTopology().getName() + FILTER_NAME_SUFFIX; + FilterType<WebAppDescriptor> filter = findFilter( context, servletName ); + // Coverity CID 1352313 + if( filter == null ) { + throw new DeploymentException( "Missing filter " + servletName ); + } else { + filter.createInitParam() + .paramName( GatewayServlet.GATEWAY_DESCRIPTOR_LOCATION_PARAM ) + .paramValue( "/WEB-INF/" + GatewayServlet.GATEWAY_DESCRIPTOR_LOCATION_DEFAULT ); + } + } + if (gatewayServices != null) { + gatewayServices.finalizeContribution(context); + } + finalizeProviders( context, providers ); + finalizeServices( context, services ); + finalizeApplications( context, application ); + writeDeploymentDescriptor( context, application != null ); + } catch ( IOException e ) { + throw new RuntimeException( e ); + } + } + + private static void finalizeProviders( DeploymentContext context, Map<String, List<ProviderDeploymentContributor>> providers ) { + if( providers != null ) { + for( Entry<String, List<ProviderDeploymentContributor>> entry : providers.entrySet() ) { + for( ProviderDeploymentContributor contributor : entry.getValue() ) { + try { + log.finalizeProvider( contributor.getName(), contributor.getRole() ); + contributor.finalizeContribution( context ); + } catch( Exception e ) { + // Maybe it makes sense to throw exception + log.failedToFinalizeContribution( e ); + throw new DeploymentException( "Failed to finalize contribution.", e ); + } + } + } + } + } + + private static void finalizeServices( DeploymentContext context, Map<String, List<ServiceDeploymentContributor>> services ) { + if( services != null ) { + for( Entry<String, List<ServiceDeploymentContributor>> entry : services.entrySet() ) { + for( ServiceDeploymentContributor contributor : entry.getValue() ) { + try { + log.finalizeService( contributor.getName(), contributor.getRole() ); + contributor.finalizeContribution( context ); + } catch( Exception e ) { + // Maybe it makes sense to throw exception + log.failedToFinalizeContribution( e ); + throw new DeploymentException( "Failed to finalize contribution.", e ); + } + } + } + } + } + + private static void finalizeApplications( DeploymentContext context, Map.Entry<String, ServiceDeploymentContributor> application ) { + if( application != null ) { + ServiceDeploymentContributor contributor = application.getValue(); + if( contributor != null ) { + try { + log.finalizeApplication( contributor.getName() ); + contributor.finalizeContribution( context ); + } catch( Exception e ) { + log.failedToInitializeContribution( e ); + throw new DeploymentException( "Failed to contribution application.", e ); + } + } + } + } + + private static void writeDeploymentDescriptor( DeploymentContext context, boolean override ) { + // Write the web.xml into the war. + Asset webXmlAsset = new StringAsset( context.getWebAppDescriptor().exportAsString() ); + if( override ) { + context.getWebArchive().addAsWebInfResource( webXmlAsset, "override-web.xml" ); + } else { + context.getWebArchive().setWebXML( webXmlAsset ); + } + } + + public static ServletType<WebAppDescriptor> findServlet( DeploymentContext context, String name ) { + List<ServletType<WebAppDescriptor>> servlets = context.getWebAppDescriptor().getAllServlet(); + for( ServletType<WebAppDescriptor> servlet : servlets ) { + if( name.equals( servlet.getServletName() ) ) { + return servlet; + } + } + return null; + } + + public static FilterType<WebAppDescriptor> findFilter( DeploymentContext context, String name ) { + List<FilterType<WebAppDescriptor>> filters = context.getWebAppDescriptor().getAllFilter(); + for( FilterType<WebAppDescriptor> filter : filters ) { + if( name.equals( filter.getFilterName() ) ) { + return filter; + } + } + return null; + } + + private static void loadStacksServiceContributors( GatewayConfig config ) { + String stacks = config.getGatewayServicesDir(); + log.usingServicesDirectory(stacks); + File stacksDir = new File(stacks); + Set<ServiceDeploymentContributor> deploymentContributors = ServiceDefinitionsLoader + .loadServiceDefinitions(stacksDir); + addServiceDeploymentContributors(deploymentContributors.iterator()); + } + + private static void loadServiceContributors() { + SERVICE_CONTRIBUTOR_MAP = new HashMap<>(); + ServiceLoader<ServiceDeploymentContributor> loader = ServiceLoader.load( ServiceDeploymentContributor.class ); + Iterator<ServiceDeploymentContributor> contributors = loader.iterator(); + addServiceDeploymentContributors(contributors); + } + + private static void addServiceDeploymentContributors(Iterator<ServiceDeploymentContributor> contributors) { + while( contributors.hasNext() ) { + ServiceDeploymentContributor contributor = contributors.next(); + if( contributor.getName() == null ) { + log.ignoringServiceContributorWithMissingName( contributor.getClass().getName() ); + continue; + } + if( contributor.getRole() == null ) { + log.ignoringServiceContributorWithMissingRole( contributor.getClass().getName() ); + continue; + } + if( contributor.getVersion() == null ) { + log.ignoringServiceContributorWithMissingVersion(contributor.getClass().getName()); + continue; + } + Map<String,Map<Version, ServiceDeploymentContributor>> nameMap = SERVICE_CONTRIBUTOR_MAP.get( contributor.getRole() ); + if( nameMap == null ) { + nameMap = new HashMap<>(); + SERVICE_CONTRIBUTOR_MAP.put( contributor.getRole(), nameMap ); + } + Map<Version, ServiceDeploymentContributor> versionMap = nameMap.get(contributor.getName()); + if (versionMap == null) { + versionMap = new TreeMap<>(); + nameMap.put(contributor.getName(), versionMap); + } + versionMap.put( contributor.getVersion(), contributor ); + } + } + + private static void loadProviderContributors() { + Set<ProviderDeploymentContributor> set = new HashSet<>(); + Map<String,Map<String,ProviderDeploymentContributor>> roleMap + = new HashMap<>(); + + ServiceLoader<ProviderDeploymentContributor> loader = ServiceLoader.load( ProviderDeploymentContributor.class ); + Iterator<ProviderDeploymentContributor> contributors = loader.iterator(); + while( contributors.hasNext() ) { + ProviderDeploymentContributor contributor = contributors.next(); + if( contributor.getName() == null ) { + log.ignoringProviderContributorWithMissingName( contributor.getClass().getName() ); + continue; + } + if( contributor.getRole() == null ) { + log.ignoringProviderContributorWithMissingRole( contributor.getClass().getName() ); + continue; + } + set.add( contributor ); + Map nameMap = roleMap.get( contributor.getRole() ); + if( nameMap == null ) { + nameMap = new HashMap<>(); + roleMap.put( contributor.getRole(), nameMap ); + } + nameMap.put( contributor.getName(), contributor ); + } + PROVIDER_CONTRIBUTORS = set; + PROVIDER_CONTRIBUTOR_MAP = roleMap; + } + + static ProviderDeploymentContributor getProviderContributor( + Map<String,List<ProviderDeploymentContributor>> providers, String role, String name ) { + ProviderDeploymentContributor contributor = null; + if( name == null ) { + List<ProviderDeploymentContributor> list = providers.get( role ); + if( list != null && !list.isEmpty() ) { + contributor = list.get( 0 ); + } + } else { + contributor = getProviderContributor( role, name ); + // Explicit configuration that is wrong should just fail + // rather than randomly select a provider. Implicit default + // providers can be selected when no name is provided. + if (contributor == null || !contributor.getRole().equals(role) || + !contributor.getName().equals(name)) { + throw new DeploymentException( + "Failed to contribute provider. Role: " + + role + " Name: " + name + ". Please check the topology for" + + " errors in name and role and that the provider is " + + "on the classpath."); + } + } + return contributor; + } +} http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/main/java/org/apache/knox/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java ---------------------------------------------------------------------- diff --cc gateway-server/src/main/java/org/apache/knox/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java index a056ac7,0000000..7e69af5 mode 100644,000000..100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java @@@ -1,256 -1,0 +1,264 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.deploy.impl; + +import org.apache.knox.gateway.config.impl.GatewayConfigImpl; +import org.apache.knox.gateway.deploy.DeploymentContext; +import org.apache.knox.gateway.deploy.ServiceDeploymentContributorBase; +import org.apache.knox.gateway.descriptor.FilterDescriptor; +import org.apache.knox.gateway.descriptor.FilterParamDescriptor; +import org.apache.knox.gateway.descriptor.ResourceDescriptor; +import org.apache.knox.gateway.dispatch.GatewayDispatchFilter; +import org.apache.knox.gateway.filter.XForwardedHeaderFilter; +import org.apache.knox.gateway.filter.rewrite.api.CookieScopeServletFilter; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor; +import org.apache.knox.gateway.service.definition.CustomDispatch; +import org.apache.knox.gateway.service.definition.Policy; +import org.apache.knox.gateway.service.definition.Rewrite; +import org.apache.knox.gateway.service.definition.Route; +import org.apache.knox.gateway.service.definition.ServiceDefinition; +import org.apache.knox.gateway.topology.Provider; +import org.apache.knox.gateway.topology.Service; +import org.apache.knox.gateway.topology.Version; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ServiceDefinitionDeploymentContributor extends ServiceDeploymentContributorBase { + + private static final String DISPATCH_ROLE = "dispatch"; + + private static final String DISPATCH_IMPL_PARAM = "dispatch-impl"; + + private static final String HTTP_CLIENT_FACTORY_PARAM = "httpClientFactory"; + + private static final String SERVICE_ROLE_PARAM = "serviceRole"; + + private static final String XFORWARDED_FILTER_NAME = "XForwardedHeaderFilter"; + + private static final String XFORWARDED_FILTER_ROLE = "xforwardedheaders"; + + private static final String DEFAULT_HA_DISPATCH_CLASS = "org.apache.knox.gateway.ha.dispatch.DefaultHaDispatch"; + + private static final String COOKIE_SCOPING_FILTER_NAME = "CookieScopeServletFilter"; + + private static final String COOKIE_SCOPING_FILTER_ROLE = "cookiescopef"; + + private ServiceDefinition serviceDefinition; + + private UrlRewriteRulesDescriptor serviceRules; + + public ServiceDefinitionDeploymentContributor(ServiceDefinition serviceDefinition, UrlRewriteRulesDescriptor serviceRules) { + this.serviceDefinition = serviceDefinition; + this.serviceRules = serviceRules; + } + + @Override + public String getRole() { + return serviceDefinition.getRole(); + } + + @Override + public String getName() { + return serviceDefinition.getName(); + } + + @Override + public Version getVersion() { + return new Version(serviceDefinition.getVersion()); + } + + @Override + public void contributeService(DeploymentContext context, Service service) throws Exception { + contributeRewriteRules(context, service); + contributeResources(context, service); + } + + private void contributeRewriteRules(DeploymentContext context, Service service) { + if ( serviceRules != null ) { + UrlRewriteRulesDescriptor clusterRules = context.getDescriptor("rewrite"); + clusterRules.addRules(serviceRules); + } + } + + private void contributeResources(DeploymentContext context, Service service) { + Map<String, String> filterParams = new HashMap<>(); + List<Route> bindings = serviceDefinition.getRoutes(); + for ( Route binding : bindings ) { + List<Rewrite> filters = binding.getRewrites(); + if ( filters != null && !filters.isEmpty() ) { + filterParams.clear(); + for ( Rewrite filter : filters ) { + filterParams.put(filter.getTo(), filter.getApply()); + } + } + try { + contributeResource(context, service, binding, filterParams); + } catch ( URISyntaxException e ) { + e.printStackTrace(); + } + } + + } + + private void contributeResource(DeploymentContext context, Service service, Route binding, Map<String, String> filterParams) throws URISyntaxException { + List<FilterParamDescriptor> params = new ArrayList<FilterParamDescriptor>(); + ResourceDescriptor resource = context.getGatewayDescriptor().addResource(); + resource.role(service.getRole()); + resource.pattern(binding.getPath()); + //add x-forwarded filter if enabled in config + if (context.getGatewayConfig().isXForwardedEnabled()) { + resource.addFilter().name(XFORWARDED_FILTER_NAME).role(XFORWARDED_FILTER_ROLE).impl(XForwardedHeaderFilter.class); + } + if (context.getGatewayConfig().isCookieScopingToPathEnabled()) { + FilterDescriptor filter = resource.addFilter().name(COOKIE_SCOPING_FILTER_NAME).role(COOKIE_SCOPING_FILTER_ROLE).impl(CookieScopeServletFilter.class); + filter.param().name(GatewayConfigImpl.HTTP_PATH).value(context.getGatewayConfig().getGatewayPath()); + } + List<Policy> policyBindings = binding.getPolicies(); + if ( policyBindings == null ) { + policyBindings = serviceDefinition.getPolicies(); + } + if ( policyBindings == null ) { + //add default set + addDefaultPolicies(context, service, filterParams, params, resource); + } else { + addPolicies(context, service, filterParams, params, resource, policyBindings); + } + addDispatchFilter(context, service, resource, binding); + } + + private void addPolicies(DeploymentContext context, Service service, Map<String, String> filterParams, List<FilterParamDescriptor> params, ResourceDescriptor resource, List<Policy> policyBindings) throws URISyntaxException { + for ( Policy policyBinding : policyBindings ) { + String role = policyBinding.getRole(); + if ( role == null ) { + throw new IllegalArgumentException("Policy defined has no role for service " + service.getName()); + } + role = role.trim().toLowerCase(); + if ( "rewrite".equals(role) ) { + addRewriteFilter(context, service, filterParams, params, resource); + } else if ( topologyContainsProviderType(context, role) ) { + context.contributeFilter(service, resource, role, policyBinding.getName(), null); + } + } + } + + private void addDefaultPolicies(DeploymentContext context, Service service, Map<String, String> filterParams, List<FilterParamDescriptor> params, ResourceDescriptor resource) throws URISyntaxException { + addWebAppSecFilters(context, service, resource); + addAuthenticationFilter(context, service, resource); + addRewriteFilter(context, service, filterParams, params, resource); + addIdentityAssertionFilter(context, service, resource); + addAuthorizationFilter(context, service, resource); + } + + private void addRewriteFilter(DeploymentContext context, Service service, Map<String, String> filterParams, List<FilterParamDescriptor> params, ResourceDescriptor resource) throws URISyntaxException { + if ( !filterParams.isEmpty() ) { + for ( Map.Entry<String, String> filterParam : filterParams.entrySet() ) { + params.add(resource.createFilterParam().name(filterParam.getKey()).value(filterParam.getValue())); + } + } + addRewriteFilter(context, service, resource, params); + } + + private void addDispatchFilter(DeploymentContext context, Service service, ResourceDescriptor resource, Route binding) { + CustomDispatch customDispatch = binding.getDispatch(); + if ( customDispatch == null ) { + customDispatch = serviceDefinition.getDispatch(); + } + boolean isHaEnabled = isHaEnabled(context); + if ( customDispatch != null ) { + String haContributorName = customDispatch.getHaContributorName(); + String haClassName = customDispatch.getHaClassName(); + String httpClientFactory = customDispatch.getHttpClientFactory(); ++ boolean useTwoWaySsl = customDispatch.getUseTwoWaySsl(); + if ( isHaEnabled) { + if (haContributorName != null) { + addDispatchFilter(context, service, resource, DISPATCH_ROLE, haContributorName); + } else if (haClassName != null) { - addDispatchFilterForClass(context, service, resource, haClassName, httpClientFactory); ++ addDispatchFilterForClass(context, service, resource, haClassName, httpClientFactory, useTwoWaySsl); + } else { + addDefaultHaDispatchFilter(context, service, resource); + } + } else { + String contributorName = customDispatch.getContributorName(); + if ( contributorName != null ) { + addDispatchFilter(context, service, resource, DISPATCH_ROLE, contributorName); + } else { + String className = customDispatch.getClassName(); + if ( className != null ) { - addDispatchFilterForClass(context, service, resource, className, httpClientFactory); ++ addDispatchFilterForClass(context, service, resource, className, httpClientFactory, useTwoWaySsl); + } else { + //final fallback to the default dispatch + addDispatchFilter(context, service, resource, DISPATCH_ROLE, "http-client"); + } + } + } + } else if (isHaEnabled) { + addDefaultHaDispatchFilter(context, service, resource); + } else { + addDispatchFilter(context, service, resource, DISPATCH_ROLE, "http-client"); + } + } + + private void addDefaultHaDispatchFilter(DeploymentContext context, Service service, ResourceDescriptor resource) { + FilterDescriptor filter = addDispatchFilterForClass(context, service, resource, DEFAULT_HA_DISPATCH_CLASS, null); + filter.param().name(SERVICE_ROLE_PARAM).value(service.getRole()); + } + - private FilterDescriptor addDispatchFilterForClass(DeploymentContext context, Service service, ResourceDescriptor resource, String dispatchClass, String httpClientFactory) { ++ private FilterDescriptor addDispatchFilterForClass(DeploymentContext context, Service service, ResourceDescriptor resource, String dispatchClass, String httpClientFactory, boolean useTwoWaySsl) { + FilterDescriptor filter = resource.addFilter().name(getName()).role(DISPATCH_ROLE).impl(GatewayDispatchFilter.class); + filter.param().name(DISPATCH_IMPL_PARAM).value(dispatchClass); + if (httpClientFactory != null) { + filter.param().name(HTTP_CLIENT_FACTORY_PARAM).value(httpClientFactory); + } ++ // let's take the value of useTwoWaySsl which is derived from the service definition ++ // then allow it to be overridden by service params from the topology ++ filter.param().name("useTwoWaySsl").value(Boolean.toString(useTwoWaySsl)); + for ( Map.Entry<String, String> serviceParam : service.getParams().entrySet() ) { + filter.param().name(serviceParam.getKey()).value(serviceParam.getValue()); + } + if ( context.getGatewayConfig().isHadoopKerberosSecured() ) { + filter.param().name("kerberos").value("true"); + } else { + //TODO: [sumit] Get rid of special case. Add config/param capabilities to service definitions? + //special case for hive + filter.param().name("basicAuthPreemptive").value("true"); + } + return filter; + } + ++ private FilterDescriptor addDispatchFilterForClass(DeploymentContext context, Service service, ResourceDescriptor resource, String dispatchClass, String httpClientFactory) { ++ return addDispatchFilterForClass(context, service, resource, dispatchClass, httpClientFactory, false); ++ } ++ + private boolean isHaEnabled(DeploymentContext context) { + Provider provider = getProviderByRole(context, "ha"); + if ( provider != null && provider.isEnabled() ) { + Map<String, String> params = provider.getParams(); + if ( params != null ) { + if ( params.containsKey(getRole()) ) { + return true; + } + } + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/main/java/org/apache/knox/gateway/services/metrics/impl/instr/InstrHttpClientBuilderProvider.java ---------------------------------------------------------------------- diff --cc gateway-server/src/main/java/org/apache/knox/gateway/services/metrics/impl/instr/InstrHttpClientBuilderProvider.java index 073adcd,0000000..1299d6f mode 100644,000000..100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/services/metrics/impl/instr/InstrHttpClientBuilderProvider.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/metrics/impl/instr/InstrHttpClientBuilderProvider.java @@@ -1,71 -1,0 +1,70 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.services.metrics.impl.instr; + +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.httpclient.HttpClientMetricNameStrategy; +import com.codahale.metrics.httpclient.InstrumentedHttpRequestExecutor; +import org.apache.knox.gateway.services.metrics.InstrumentationProvider; +import org.apache.knox.gateway.services.metrics.MetricsContext; +import org.apache.knox.gateway.services.metrics.impl.DefaultMetricsService; +import org.apache.http.Header; +import org.apache.http.HttpRequest; +import org.apache.http.RequestLine; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; + +import java.net.URISyntaxException; + +public class InstrHttpClientBuilderProvider implements + InstrumentationProvider<HttpClientBuilder> { + + @Override + public HttpClientBuilder getInstrumented(MetricsContext metricsContext) { + MetricRegistry registry = (MetricRegistry) metricsContext.getProperty(DefaultMetricsService.METRICS_REGISTRY); - return HttpClientBuilder.create().setRequestExecutor(new InstrumentedHttpRequestExecutor(registry, TOPOLOGY_URL_AND_METHOD)). - setConnectionManager(new PoolingHttpClientConnectionManager()); ++ return HttpClientBuilder.create().setRequestExecutor(new InstrumentedHttpRequestExecutor(registry, TOPOLOGY_URL_AND_METHOD)); + } + + @Override + public HttpClientBuilder getInstrumented(HttpClientBuilder instanceClass, MetricsContext metricsContext) { + throw new UnsupportedOperationException(); + } + + private static final HttpClientMetricNameStrategy TOPOLOGY_URL_AND_METHOD = new HttpClientMetricNameStrategy() { + public String getNameFor(String name, HttpRequest request) { + try { + String context = ""; + Header header = request.getFirstHeader("X-Forwarded-Context"); + if (header != null) { + context = header.getValue(); + } + RequestLine requestLine = request.getRequestLine(); + URIBuilder uriBuilder = new URIBuilder(requestLine.getUri()); + String resourcePath = InstrUtils.getResourcePath(uriBuilder.removeQuery().build().toString()); + return MetricRegistry.name("service", new String[]{name, context + resourcePath, methodNameString(request)}); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + + private String methodNameString(HttpRequest request) { + return request.getRequestLine().getMethod().toLowerCase() + "-requests"; + } + }; +} http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/main/java/org/apache/knox/gateway/topology/builder/BeanPropertyTopologyBuilder.java ---------------------------------------------------------------------- diff --cc gateway-server/src/main/java/org/apache/knox/gateway/topology/builder/BeanPropertyTopologyBuilder.java index a1a2609,0000000..afeade0 mode 100644,000000..100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/topology/builder/BeanPropertyTopologyBuilder.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/topology/builder/BeanPropertyTopologyBuilder.java @@@ -1,105 -1,0 +1,116 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.knox.gateway.topology.builder; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.knox.gateway.topology.Application; +import org.apache.knox.gateway.topology.Provider; +import org.apache.knox.gateway.topology.Service; +import org.apache.knox.gateway.topology.Topology; + +public class BeanPropertyTopologyBuilder implements TopologyBuilder { + + private String name; + private String defaultService; ++ private boolean isGenerated; + private List<Provider> providers; + private List<Service> services; + private List<Application> applications; + + public BeanPropertyTopologyBuilder() { + providers = new ArrayList<Provider>(); + services = new ArrayList<Service>(); + applications = new ArrayList<Application>(); + } + + public BeanPropertyTopologyBuilder name(String name) { + this.name = name; + return this; + } + + public String name() { + return name; + } + ++ public BeanPropertyTopologyBuilder generated(String isGenerated) { ++ this.isGenerated = Boolean.valueOf(isGenerated); ++ return this; ++ } ++ ++ public boolean isGenerated() { ++ return isGenerated; ++ } ++ + public BeanPropertyTopologyBuilder defaultService(String defaultService) { + this.defaultService = defaultService; + return this; + } + + public String defaultService() { + return defaultService; + } + + public BeanPropertyTopologyBuilder addProvider(Provider provider) { + providers.add(provider); + return this; + } + + public List<Provider> providers() { + return providers; + } + + public BeanPropertyTopologyBuilder addService(Service service) { + services.add(service); + return this; + } + + public List<Service> services() { + return services; + } + + public BeanPropertyTopologyBuilder addApplication( Application application ) { + applications.add(application); + return this; + } + + public List<Application> applications() { + return applications; + } + + public Topology build() { + Topology topology = new Topology(); + topology.setName(name); + topology.setDefaultServicePath(defaultService); ++ topology.setGenerated(isGenerated); + + for (Provider provider : providers) { + topology.addProvider(provider); + } + + for (Service service : services) { + topology.addService(service); + } + + for (Application application : applications) { + topology.addApplication(application); + } + + return topology; + } +} http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptor.java ---------------------------------------------------------------------- diff --cc gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptor.java index 25997b1,0000000..7d25286 mode 100644,000000..100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptor.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptor.java @@@ -1,48 -1,0 +1,58 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.knox.gateway.topology.simple; + +import java.util.List; +import java.util.Map; + +public interface SimpleDescriptor { + + String getName(); + + String getDiscoveryType(); + + String getDiscoveryAddress(); + + String getDiscoveryUser(); + + String getDiscoveryPasswordAlias(); + + String getClusterName(); + + String getProviderConfig(); + + List<Service> getServices(); + ++ List<Application> getApplications(); ++ + + interface Service { + String getName(); + + Map<String, String> getParams(); + + List<String> getURLs(); + } ++ ++ interface Application { ++ String getName(); ++ ++ Map<String, String> getParams(); ++ ++ List<String> getURLs(); ++ } +} http://git-wip-us.apache.org/repos/asf/knox/blob/2c69152f/gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java ---------------------------------------------------------------------- diff --cc gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java index b54432d,0000000..2e3214d mode 100644,000000..100644 --- a/gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java +++ b/gateway-server/src/main/java/org/apache/knox/gateway/topology/simple/SimpleDescriptorHandler.java @@@ -1,267 -1,0 +1,316 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.knox.gateway.topology.simple; + +import org.apache.knox.gateway.i18n.messages.MessagesFactory; +import org.apache.knox.gateway.services.Service; +import org.apache.knox.gateway.topology.discovery.DefaultServiceDiscoveryConfig; +import org.apache.knox.gateway.topology.discovery.ServiceDiscovery; +import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryFactory; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.io.IOException; + +import java.net.URI; +import java.net.URISyntaxException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + + +/** + * Processes simple topology descriptors, producing full topology files, which can subsequently be deployed to the + * gateway. + */ +public class SimpleDescriptorHandler { + + private static final Service[] NO_GATEWAY_SERVICES = new Service[]{}; + + private static final SimpleDescriptorMessages log = MessagesFactory.get(SimpleDescriptorMessages.class); + + public static Map<String, File> handle(File desc) throws IOException { + return handle(desc, NO_GATEWAY_SERVICES); + } + + public static Map<String, File> handle(File desc, Service...gatewayServices) throws IOException { + return handle(desc, desc.getParentFile(), gatewayServices); + } + + public static Map<String, File> handle(File desc, File destDirectory) throws IOException { + return handle(desc, destDirectory, NO_GATEWAY_SERVICES); + } + + public static Map<String, File> handle(File desc, File destDirectory, Service...gatewayServices) throws IOException { + return handle(SimpleDescriptorFactory.parse(desc.getAbsolutePath()), desc.getParentFile(), destDirectory, gatewayServices); + } + + public static Map<String, File> handle(SimpleDescriptor desc, File srcDirectory, File destDirectory) { + return handle(desc, srcDirectory, destDirectory, NO_GATEWAY_SERVICES); + } + + public static Map<String, File> handle(SimpleDescriptor desc, File srcDirectory, File destDirectory, Service...gatewayServices) { + Map<String, File> result = new HashMap<>(); + + File topologyDescriptor; + + DefaultServiceDiscoveryConfig sdc = new DefaultServiceDiscoveryConfig(desc.getDiscoveryAddress()); + sdc.setUser(desc.getDiscoveryUser()); + sdc.setPasswordAlias(desc.getDiscoveryPasswordAlias()); - ServiceDiscovery sd = ServiceDiscoveryFactory.get(desc.getDiscoveryType(), gatewayServices); ++ ++ // Use the discovery type from the descriptor. If it's unspecified, employ the default type. ++ String discoveryType = desc.getDiscoveryType(); ++ if (discoveryType == null) { ++ discoveryType = "AMBARI"; ++ } ++ ++ ServiceDiscovery sd = ServiceDiscoveryFactory.get(discoveryType, gatewayServices); + ServiceDiscovery.Cluster cluster = sd.discover(sdc, desc.getClusterName()); + + List<String> validServiceNames = new ArrayList<>(); + + Map<String, Map<String, String>> serviceParams = new HashMap<>(); + Map<String, List<String>> serviceURLs = new HashMap<>(); + + if (cluster != null) { + for (SimpleDescriptor.Service descService : desc.getServices()) { + String serviceName = descService.getName(); + + List<String> descServiceURLs = descService.getURLs(); + if (descServiceURLs == null || descServiceURLs.isEmpty()) { + descServiceURLs = cluster.getServiceURLs(serviceName); + } + + // Validate the discovered service URLs + List<String> validURLs = new ArrayList<>(); + if (descServiceURLs != null && !descServiceURLs.isEmpty()) { + // Validate the URL(s) + for (String descServiceURL : descServiceURLs) { + if (validateURL(serviceName, descServiceURL)) { + validURLs.add(descServiceURL); + } + } + + if (!validURLs.isEmpty()) { + validServiceNames.add(serviceName); + } + } + + // If there is at least one valid URL associated with the service, then add it to the map + if (!validURLs.isEmpty()) { + serviceURLs.put(serviceName, validURLs); + } else { + log.failedToDiscoverClusterServiceURLs(serviceName, cluster.getName()); + } + + // Service params + if (descService.getParams() != null) { + serviceParams.put(serviceName, descService.getParams()); + if (!validServiceNames.contains(serviceName)) { + validServiceNames.add(serviceName); + } + } + } + } else { + log.failedToDiscoverClusterServices(desc.getClusterName()); + } + + BufferedWriter fw = null; + topologyDescriptor = null; + File providerConfig; + try { + // Verify that the referenced provider configuration exists before attempting to reading it + providerConfig = resolveProviderConfigurationReference(desc.getProviderConfig(), srcDirectory); + if (providerConfig == null) { + log.failedToResolveProviderConfigRef(desc.getProviderConfig()); + throw new IllegalArgumentException("Unresolved provider configuration reference: " + + desc.getProviderConfig() + " ; Topology update aborted!"); + } + result.put("reference", providerConfig); + + // TODO: Should the contents of the provider config be validated before incorporating it into the topology? + + String topologyFilename = desc.getName(); + if (topologyFilename == null) { + topologyFilename = desc.getClusterName(); + } + topologyDescriptor = new File(destDirectory, topologyFilename + ".xml"); ++ + fw = new BufferedWriter(new FileWriter(topologyDescriptor)); + ++ fw.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); ++ ++ fw.write("<!--==============================================-->\n"); ++ fw.write("<!-- DO NOT EDIT. This is an auto-generated file. -->\n"); ++ fw.write("<!--==============================================-->\n"); ++ + fw.write("<topology>\n"); + ++ // KNOX-1105 Indicate that this topology was auto-generated ++ fw.write(" <generated>true</generated>\n"); ++ + // Copy the externalized provider configuration content into the topology descriptor in-line + InputStreamReader policyReader = new InputStreamReader(new FileInputStream(providerConfig)); + char[] buffer = new char[1024]; + int count; + while ((count = policyReader.read(buffer)) > 0) { + fw.write(buffer, 0, count); + } + policyReader.close(); + ++ // Services + // Sort the service names to write the services alphabetically + List<String> serviceNames = new ArrayList<>(validServiceNames); + Collections.sort(serviceNames); + + // Write the service declarations + for (String serviceName : serviceNames) { + fw.write(" <service>\n"); + fw.write(" <role>" + serviceName + "</role>\n"); + + // URLs + List<String> urls = serviceURLs.get(serviceName); + if (urls != null) { + for (String url : urls) { + fw.write(" <url>" + url + "</url>\n"); + } + } + + // Params + Map<String, String> svcParams = serviceParams.get(serviceName); + if (svcParams != null) { + for (String paramName : svcParams.keySet()) { + fw.write(" <param>\n"); + fw.write(" <name>" + paramName + "</name>\n"); + fw.write(" <value>" + svcParams.get(paramName) + "</value>\n"); + fw.write(" </param>\n"); + } + } + + fw.write(" </service>\n"); + } + ++ // Applications ++ List<SimpleDescriptor.Application> apps = desc.getApplications(); ++ if (apps != null) { ++ for (SimpleDescriptor.Application app : apps) { ++ fw.write(" <application>\n"); ++ fw.write(" <name>" + app.getName() + "</name>\n"); ++ ++ // URLs ++ List<String> urls = app.getURLs(); ++ if (urls != null) { ++ for (String url : urls) { ++ fw.write(" <url>" + url + "</url>\n"); ++ } ++ } ++ ++ // Params ++ Map<String, String> appParams = app.getParams(); ++ if (appParams != null) { ++ for (String paramName : appParams.keySet()) { ++ fw.write(" <param>\n"); ++ fw.write(" <name>" + paramName + "</name>\n"); ++ fw.write(" <value>" + appParams.get(paramName) + "</value>\n"); ++ fw.write(" </param>\n"); ++ } ++ } ++ ++ fw.write(" </application>\n"); ++ } ++ } ++ + fw.write("</topology>\n"); + + fw.flush(); + } catch (IOException e) { + log.failedToGenerateTopologyFromSimpleDescriptor(topologyDescriptor.getName(), e); + topologyDescriptor.delete(); + } finally { + if (fw != null) { + try { + fw.close(); + } catch (IOException e) { + // ignore + } + } + } + + result.put("topology", topologyDescriptor); + return result; + } + ++ + private static boolean validateURL(String serviceName, String url) { + boolean result = false; + + if (url != null && !url.isEmpty()) { + try { + new URI(url); + result = true; + } catch (URISyntaxException e) { + log.serviceURLValidationFailed(serviceName, url, e); + } + } + + return result; + } + + + private static File resolveProviderConfigurationReference(String reference, File srcDirectory) { + File providerConfig; + + // If the reference includes a path + if (reference.contains(File.separator)) { + // Check if it's an absolute path + providerConfig = new File(reference); + if (!providerConfig.exists()) { + // If it's not an absolute path, try treating it as a relative path + providerConfig = new File(srcDirectory, reference); + if (!providerConfig.exists()) { + providerConfig = null; + } + } + } else { // No file path, just a name + // Check if it's co-located with the referencing descriptor + providerConfig = new File(srcDirectory, reference); + if (!providerConfig.exists()) { + // Check the shared-providers config location + File sharedProvidersDir = new File(srcDirectory, "../shared-providers"); + if (sharedProvidersDir.exists()) { + providerConfig = new File(sharedProvidersDir, reference); + if (!providerConfig.exists()) { + // Check if it's a valid name without the extension + providerConfig = new File(sharedProvidersDir, reference + ".xml"); + if (!providerConfig.exists()) { + providerConfig = null; + } + } + } + } + } + + return providerConfig; + } + +}
