From 9cb5f3da89b26cc60725a7e0a0c300a53d38f91f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 8 Jan 2018 21:34:53 -0800 Subject: [PATCH] Create DynamicRegistrationBean Extract functionality from the `RegistrationBean` into a new class designed to work with dynamic registration. Servet and Filter registration beans now extend from `DynaimcRegistrationBean`, where as `ServletListenerRegistrationBean` extends directly from `RegistrationBean`. This refactor allows the removal of `ServletListenerRegistrationBean` deprecated methods. Fixes gh-11344 --- .../jersey/JerseyAutoConfiguration.java | 6 +- .../AbstractFilterRegistrationBean.java | 39 +++-- .../web/servlet/DynaimcRegistrationBean.java | 141 ++++++++++++++++++ .../boot/web/servlet/RegistrationBean.java | 106 +++---------- .../ServletContextInitializerBeans.java | 12 +- .../ServletListenerRegistrationBean.java | 89 ++--------- .../web/servlet/ServletRegistrationBean.java | 43 +++--- .../ui/SampleActuatorUiApplication.java | 2 +- 8 files changed, 221 insertions(+), 217 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/DynaimcRegistrationBean.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java index 29b202af6b80..78ede63c7a8a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jersey/JerseyAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,8 +56,8 @@ import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.DynaimcRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.boot.web.servlet.RegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -181,7 +181,7 @@ private String getServletRegistrationName() { return ClassUtils.getUserClass(this.config.getClass()).getName(); } - private void addInitParameters(RegistrationBean registration) { + private void addInitParameters(DynaimcRegistrationBean registration) { for (Entry entry : this.jersey.getInit().entrySet()) { registration.addInitParameter(entry.getKey(), entry.getValue()); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/AbstractFilterRegistrationBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/AbstractFilterRegistrationBean.java index ce02d8f5d101..03edfd7f430a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/AbstractFilterRegistrationBean.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/AbstractFilterRegistrationBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,8 +26,8 @@ import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterRegistration; +import javax.servlet.FilterRegistration.Dynamic; import javax.servlet.ServletContext; -import javax.servlet.ServletException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -41,7 +41,8 @@ * @param the type of {@link Filter} to register * @author Phillip Webb */ -abstract class AbstractFilterRegistrationBean extends RegistrationBean { +abstract class AbstractFilterRegistrationBean + extends DynaimcRegistrationBean { /** * Filters that wrap the servlet request should be ordered less than or equal to this. @@ -208,34 +209,24 @@ public boolean isMatchAfter() { } @Override - public void onStartup(ServletContext servletContext) throws ServletException { + protected String getDescription() { Filter filter = getFilter(); Assert.notNull(filter, "Filter must not be null"); - String name = getOrDeduceName(filter); - if (!isEnabled()) { - this.logger.info("Filter " + name + " was not registered (disabled)"); - return; - } - FilterRegistration.Dynamic added = servletContext.addFilter(name, filter); - if (added == null) { - this.logger.info("Filter " + name + " was not registered " - + "(possibly already registered?)"); - return; - } - configure(added); + return "filter " + getOrDeduceName(filter); } - /** - * Return the {@link Filter} to be registered. - * @return the filter - */ - public abstract T getFilter(); + @Override + protected Dynamic addRegistration(String description, ServletContext servletContext) { + Filter filter = getFilter(); + return servletContext.addFilter(getOrDeduceName(filter), filter); + } /** * Configure registration settings. Subclasses can override this method to perform * additional configuration if required. * @param registration the registration */ + @Override protected void configure(FilterRegistration.Dynamic registration) { super.configure(registration); EnumSet dispatcherTypes = this.dispatcherTypes; @@ -269,4 +260,10 @@ protected void configure(FilterRegistration.Dynamic registration) { } } + /** + * Return the {@link Filter} to be registered. + * @return the filter + */ + public abstract T getFilter(); + } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/DynaimcRegistrationBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/DynaimcRegistrationBean.java new file mode 100644 index 000000000000..e979ca2690b8 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/DynaimcRegistrationBean.java @@ -0,0 +1,141 @@ +/* + * Copyright 2012-2018 the original author or authors. + * + * Licensed 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.springframework.boot.web.servlet; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.servlet.Registration; +import javax.servlet.ServletContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.core.Conventions; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Base class for Servlet 3.0+ {@link javax.servlet.Registration.Dynamic dynamic} based + * registration beans. + * + * @param The dynamic registration result + * @author Phillip Webb + * @since 2.0.0 + */ +public abstract class DynaimcRegistrationBean + extends RegistrationBean { + + private static final Log logger = LogFactory.getLog(RegistrationBean.class); + + private String name; + + private boolean asyncSupported = true; + + private Map initParameters = new LinkedHashMap<>(); + + /** + * Set the name of this registration. If not specified the bean name will be used. + * @param name the name of the registration + */ + public void setName(String name) { + Assert.hasLength(name, "Name must not be empty"); + this.name = name; + } + + /** + * Sets if asynchronous operations are support for this registration. If not specified + * defaults to {@code true}. + * @param asyncSupported if async is supported + */ + public void setAsyncSupported(boolean asyncSupported) { + this.asyncSupported = asyncSupported; + } + + /** + * Returns if asynchronous operations are support for this registration. + * @return if async is supported + */ + public boolean isAsyncSupported() { + return this.asyncSupported; + } + + /** + * Set init-parameters for this registration. Calling this method will replace any + * existing init-parameters. + * @param initParameters the init parameters + * @see #getInitParameters + * @see #addInitParameter + */ + public void setInitParameters(Map initParameters) { + Assert.notNull(initParameters, "InitParameters must not be null"); + this.initParameters = new LinkedHashMap<>(initParameters); + } + + /** + * Returns a mutable Map of the registration init-parameters. + * @return the init parameters + */ + public Map getInitParameters() { + return this.initParameters; + } + + /** + * Add a single init-parameter, replacing any existing parameter with the same name. + * @param name the init-parameter name + * @param value the init-parameter value + */ + public void addInitParameter(String name, String value) { + Assert.notNull(name, "Name must not be null"); + this.initParameters.put(name, value); + } + + @Override + protected final void register(String description, ServletContext servletContext) { + D registration = addRegistration(description, servletContext); + if (registration == null) { + logger.info(StringUtils.capitalize(description) + " was not registered " + + "(possibly already registered?)"); + return; + } + Assert.state(registration != null, + () -> "Registration is null. Was something already registered for " + + description + "?"); + configure(registration); + } + + protected abstract D addRegistration(String description, + ServletContext servletContext); + + protected void configure(D registration) { + registration.setAsyncSupported(this.asyncSupported); + if (!this.initParameters.isEmpty()) { + registration.setInitParameters(this.initParameters); + } + } + + /** + * Deduces the name for this registration. Will return user specified name or fallback + * to convention based naming. + * @param value the object used for convention based names + * @return the deduced name + */ + protected final String getOrDeduceName(Object value) { + return (this.name != null ? this.name : Conventions.getVariableName(value)); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/RegistrationBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/RegistrationBean.java index 38da4c499571..be88cf13a4fc 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/RegistrationBean.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/RegistrationBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,14 @@ package org.springframework.boot.web.servlet; -import java.util.LinkedHashMap; -import java.util.Map; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; -import javax.servlet.Registration; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; -import org.springframework.core.Conventions; import org.springframework.core.Ordered; -import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Base class for Servlet 3.0+ based registration beans. @@ -37,41 +37,35 @@ */ public abstract class RegistrationBean implements ServletContextInitializer, Ordered { - private String name; + private static final Log logger = LogFactory.getLog(RegistrationBean.class); private int order = Ordered.LOWEST_PRECEDENCE; - private boolean asyncSupported = true; - private boolean enabled = true; - private Map initParameters = new LinkedHashMap<>(); - - /** - * Set the name of this registration. If not specified the bean name will be used. - * @param name the name of the registration - */ - public void setName(String name) { - Assert.hasLength(name, "Name must not be empty"); - this.name = name; + @Override + public final void onStartup(ServletContext servletContext) throws ServletException { + String description = getDescription(); + if (!isEnabled()) { + logger.info(StringUtils.capitalize(description) + + " was not registered (disabled)"); + return; + } + register(description, servletContext); } /** - * Sets if asynchronous operations are support for this registration. If not specified - * defaults to {@code true}. - * @param asyncSupported if async is supported + * Return a description of the registration. For example "Servlet resourceServlet" + * @return a description of the registration */ - public void setAsyncSupported(boolean asyncSupported) { - this.asyncSupported = asyncSupported; - } + protected abstract String getDescription(); /** - * Returns if asynchronous operations are support for this registration. - * @return if async is supported + * Register this bean with the servlet context. + * @param description a description of the item being registered + * @param servletContext the servlet context */ - public boolean isAsyncSupported() { - return this.asyncSupported; - } + protected abstract void register(String description, ServletContext servletContext); /** * Flag to indicate that the registration is enabled. @@ -89,60 +83,6 @@ public boolean isEnabled() { return this.enabled; } - /** - * Set init-parameters for this registration. Calling this method will replace any - * existing init-parameters. - * @param initParameters the init parameters - * @see #getInitParameters - * @see #addInitParameter - */ - public void setInitParameters(Map initParameters) { - Assert.notNull(initParameters, "InitParameters must not be null"); - this.initParameters = new LinkedHashMap<>(initParameters); - } - - /** - * Returns a mutable Map of the registration init-parameters. - * @return the init parameters - */ - public Map getInitParameters() { - return this.initParameters; - } - - /** - * Add a single init-parameter, replacing any existing parameter with the same name. - * @param name the init-parameter name - * @param value the init-parameter value - */ - public void addInitParameter(String name, String value) { - Assert.notNull(name, "Name must not be null"); - this.initParameters.put(name, value); - } - - /** - * Deduces the name for this registration. Will return user specified name or fallback - * to convention based naming. - * @param value the object used for convention based names - * @return the deduced name - */ - protected final String getOrDeduceName(Object value) { - return (this.name != null ? this.name : Conventions.getVariableName(value)); - } - - /** - * Configure registration base settings. - * @param registration the registration - */ - protected void configure(Registration.Dynamic registration) { - Assert.state(registration != null, - () -> "Registration is null. Was something already registered for name=[" - + this.name + "]?"); - registration.setAsyncSupported(this.asyncSupported); - if (!this.initParameters.isEmpty()) { - registration.setInitParameters(this.initParameters); - } - } - /** * Set the order of the registration bean. * @param order the order diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletContextInitializerBeans.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletContextInitializerBeans.java index ee02b375c389..d50ef51b7e59 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletContextInitializerBeans.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletContextInitializerBeans.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -188,7 +188,6 @@ private void addAsRegistrationBean(ListableBeanFactory beanFact // One that we haven't already seen RegistrationBean registration = adapter.createRegistrationBean(beanName, bean.getValue(), beans.size()); - registration.setName(beanName); registration.setOrder(order); this.initializers.add(type, registration); if (ServletContextInitializerBeans.logger.isDebugEnabled()) { @@ -247,8 +246,8 @@ public int size() { } /** - * Adapter to convert a given Bean type into a {@link RegistrationBean} (and hence a - * {@link ServletContextInitializer}. + * Adapter to convert a given Bean type into a {@link DynaimcRegistrationBean} (and + * hence a {@link ServletContextInitializer}. */ private interface RegistrationBeanAdapter { @@ -278,6 +277,7 @@ public RegistrationBean createRegistrationBean(String name, Servlet source, } ServletRegistrationBean bean = new ServletRegistrationBean<>(source, url); + bean.setName(name); bean.setMultipartConfig(this.multipartConfig); return bean; } @@ -293,7 +293,9 @@ private static class FilterRegistrationBeanAdapter @Override public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) { - return new FilterRegistrationBean<>(source); + FilterRegistrationBean bean = new FilterRegistrationBean<>(source); + bean.setName(name); + return bean; } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletListenerRegistrationBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletListenerRegistrationBean.java index a26d5d84c11e..1a824ae9912f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletListenerRegistrationBean.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletListenerRegistrationBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,21 +19,16 @@ import java.util.Collections; import java.util.EventListener; import java.util.HashSet; -import java.util.Map; import java.util.Set; import javax.servlet.ServletContext; import javax.servlet.ServletContextAttributeListener; import javax.servlet.ServletContextListener; -import javax.servlet.ServletException; import javax.servlet.ServletRequestAttributeListener; import javax.servlet.ServletRequestListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionListener; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -61,9 +56,6 @@ public class ServletListenerRegistrationBean extends RegistrationBean { - private static final Log logger = LogFactory - .getLog(ServletListenerRegistrationBean.class); - private static final Set> SUPPORTED_TYPES; static { @@ -106,80 +98,21 @@ public void setListener(T listener) { } /** - * Set the name of this registration. If not specified the bean name will be used. - * @param name the name of the registration - * @deprecated as of 1.5 since not applicable to listeners - */ - @Override - @Deprecated - public void setName(String name) { - super.setName(name); - } - - /** - * Sets if asynchronous operations are support for this registration. If not specified - * defaults to {@code true}. - * @param asyncSupported if async is supported - * @deprecated as of 1.5 since not applicable to listeners - */ - @Override - @Deprecated - public void setAsyncSupported(boolean asyncSupported) { - super.setAsyncSupported(asyncSupported); - } - - /** - * Returns if asynchronous operations are support for this registration. - * @return if async is supported - * @deprecated as of 1.5 since not applicable to listeners - */ - @Override - @Deprecated - public boolean isAsyncSupported() { - return super.isAsyncSupported(); - } - - /** - * Set init-parameters for this registration. Calling this method will replace any - * existing init-parameters. - * @param initParameters the init parameters - * @deprecated as of 1.5 since not applicable to listeners - */ - @Override - @Deprecated - public void setInitParameters(Map initParameters) { - super.setInitParameters(initParameters); - } - - /** - * Returns a mutable Map of the registration init-parameters. - * @return the init parameters - * @deprecated as of 1.5 since not applicable to listeners + * Return the listener to be registered. + * @return the listener to be registered */ - @Override - @Deprecated - public Map getInitParameters() { - return super.getInitParameters(); + public T getListener() { + return this.listener; } - /** - * Add a single init-parameter, replacing any existing parameter with the same name. - * @param name the init-parameter name - * @param value the init-parameter value - * @deprecated as of 1.5 since not applicable to listeners - */ @Override - @Deprecated - public void addInitParameter(String name, String value) { - super.addInitParameter(name, value); + protected String getDescription() { + Assert.notNull(this.listener, "Listener must not be null"); + return "listener " + this.listener; } @Override - public void onStartup(ServletContext servletContext) throws ServletException { - if (!isEnabled()) { - logger.info("Listener " + this.listener + " was not registered (disabled)"); - return; - } + protected void register(String description, ServletContext servletContext) { try { servletContext.addListener(this.listener); } @@ -190,10 +123,6 @@ public void onStartup(ServletContext servletContext) throws ServletException { } } - public T getListener() { - return this.listener; - } - /** * Returns {@code true} if the specified listener is one of the supported types. * @param listener the listener to test diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletRegistrationBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletRegistrationBean.java index 65e57845f3c3..f710e9242dc7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletRegistrationBean.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletRegistrationBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,9 +24,7 @@ import javax.servlet.MultipartConfigElement; import javax.servlet.Servlet; import javax.servlet.ServletContext; -import javax.servlet.ServletException; import javax.servlet.ServletRegistration; -import javax.servlet.ServletRegistration.Dynamic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -52,7 +50,8 @@ * @see ServletContextInitializer * @see ServletContext#addServlet(String, Servlet) */ -public class ServletRegistrationBean extends RegistrationBean { +public class ServletRegistrationBean + extends DynaimcRegistrationBean { private static final Log logger = LogFactory.getLog(ServletRegistrationBean.class); @@ -172,30 +171,18 @@ public MultipartConfigElement getMultipartConfig() { return this.multipartConfig; } - /** - * Returns the servlet name that will be registered. - * @return the servlet name - */ - public String getServletName() { - return getOrDeduceName(this.servlet); + @Override + protected String getDescription() { + Assert.notNull(this.servlet, "Servlet must not be null"); + return "servlet " + getServletName(); } @Override - public void onStartup(ServletContext servletContext) throws ServletException { - Assert.notNull(this.servlet, "Servlet must not be null"); + protected ServletRegistration.Dynamic addRegistration(String description, + ServletContext servletContext) { String name = getServletName(); - if (!isEnabled()) { - logger.info("Servlet " + name + " was not registered (disabled)"); - return; - } - logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings); - Dynamic added = servletContext.addServlet(name, this.servlet); - if (added == null) { - logger.info("Servlet " + name + " was not registered " - + "(possibly already registered?)"); - return; - } - configure(added); + logger.info("Servlet " + name + " mapped to " + this.urlMappings); + return servletContext.addServlet(name, this.servlet); } /** @@ -203,6 +190,7 @@ public void onStartup(ServletContext servletContext) throws ServletException { * additional configuration if required. * @param registration the registration */ + @Override protected void configure(ServletRegistration.Dynamic registration) { super.configure(registration); String[] urlMapping = this.urlMappings @@ -219,4 +207,11 @@ protected void configure(ServletRegistration.Dynamic registration) { } } + /** + * Returns the servlet name that will be registered. + * @return the servlet name + */ + public String getServletName() { + return getOrDeduceName(this.servlet); + } } diff --git a/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/sample/actuator/ui/SampleActuatorUiApplication.java b/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/sample/actuator/ui/SampleActuatorUiApplication.java index e85d9422ef2f..684a5af5cc51 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/sample/actuator/ui/SampleActuatorUiApplication.java +++ b/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/sample/actuator/ui/SampleActuatorUiApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.