View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.annotations;
20  
21  import java.util.ArrayList;
22  
23  import javax.servlet.annotation.WebInitParam;
24  import javax.servlet.annotation.WebServlet;
25  import javax.servlet.http.HttpServlet;
26  
27  import org.eclipse.jetty.servlet.Holder;
28  import org.eclipse.jetty.servlet.ServletHolder;
29  import org.eclipse.jetty.servlet.ServletMapping;
30  import org.eclipse.jetty.util.LazyList;
31  import org.eclipse.jetty.util.log.Log;
32  import org.eclipse.jetty.util.log.Logger;
33  import org.eclipse.jetty.util.resource.Resource;
34  import org.eclipse.jetty.webapp.DiscoveredAnnotation;
35  import org.eclipse.jetty.webapp.MetaData;
36  import org.eclipse.jetty.webapp.Origin;
37  import org.eclipse.jetty.webapp.WebAppContext;
38  
39  /**
40   * WebServletAnnotation
41   *
42   *
43   */
44  public class WebServletAnnotation extends DiscoveredAnnotation
45  {
46      private static final Logger LOG = Log.getLogger(WebServletAnnotation.class);
47  
48      public WebServletAnnotation (WebAppContext context, String className)
49      {
50          super(context, className);
51      }
52      
53      
54      public WebServletAnnotation (WebAppContext context, String className, Resource resource)
55      {
56          super(context, className, resource);
57      }
58      
59      /**
60       * @see DiscoveredAnnotation#apply()
61       */
62      public void apply()
63      {
64          //TODO check this algorithm with new rules for applying descriptors and annotations in order
65          Class clazz = getTargetClass();
66  
67          if (clazz == null)
68          {
69              LOG.warn(_className+" cannot be loaded");
70              return;
71          }
72  
73          //Servlet Spec 8.1.1
74          if (!HttpServlet.class.isAssignableFrom(clazz))
75          {
76              LOG.warn(clazz.getName()+" is not assignable from javax.servlet.http.HttpServlet");
77              return;
78          }
79  
80          WebServlet annotation = (WebServlet)clazz.getAnnotation(WebServlet.class);
81  
82          if (annotation.urlPatterns().length > 0 && annotation.value().length > 0)
83          {
84              LOG.warn(clazz.getName()+ " defines both @WebServlet.value and @WebServlet.urlPatterns");
85              return;
86          }
87  
88          String[] urlPatterns = annotation.value();
89          if (urlPatterns.length == 0)
90              urlPatterns = annotation.urlPatterns();
91  
92          if (urlPatterns.length == 0)
93          {
94              LOG.warn(clazz.getName()+ " defines neither @WebServlet.value nor @WebServlet.urlPatterns");
95              return;
96          }
97  
98          //canonicalize the patterns
99          ArrayList<String> urlPatternList = new ArrayList<String>();
100         for (String p : urlPatterns)
101             urlPatternList.add(Util.normalizePattern(p));
102 
103         String servletName = (annotation.name().equals("")?clazz.getName():annotation.name());
104 
105         MetaData metaData = _context.getMetaData();
106 
107         //Find out if a <servlet> already exists with this name
108         ServletHolder[] holders = _context.getServletHandler().getServlets();
109         boolean isNew = true;
110         ServletHolder holder = null;
111         if (holders != null)
112         {
113             for (ServletHolder h : holders)
114             {
115                 if (h.getName() != null && servletName.equals(h.getName()))
116                 {
117                     holder = h;
118                     isNew = false;
119                     break;
120                 }
121             }
122         }
123 
124         if (isNew)
125         {
126             //No servlet of this name has already been defined, either by a descriptor
127             //or another annotation (which would be impossible).
128             holder = _context.getServletHandler().newServletHolder(Holder.Source.ANNOTATION);
129             holder.setHeldClass(clazz);
130             metaData.setOrigin(servletName+".servlet.servlet-class");
131 
132             holder.setName(servletName);
133             holder.setDisplayName(annotation.displayName());
134             metaData.setOrigin(servletName+".servlet.display-name");
135 
136             holder.setInitOrder(annotation.loadOnStartup());
137             metaData.setOrigin(servletName+".servlet.load-on-startup");
138 
139             holder.setAsyncSupported(annotation.asyncSupported());
140             metaData.setOrigin(servletName+".servlet.async-supported");
141 
142             for (WebInitParam ip:annotation.initParams())
143             {
144                 holder.setInitParameter(ip.name(), ip.value());
145                 metaData.setOrigin(servletName+".servlet.init-param."+ip.name());
146             }
147 
148             _context.getServletHandler().addServlet(holder);
149             ServletMapping mapping = new ServletMapping();
150             mapping.setServletName(holder.getName());
151             mapping.setPathSpecs( LazyList.toStringArray(urlPatternList));
152             _context.getServletHandler().addServletMapping(mapping);
153             metaData.setOrigin(servletName+".servlet.mappings");
154         }
155         else
156         {
157             //set the class according to the servlet that is annotated, if it wasn't already
158             //NOTE: this may be considered as "completing" an incomplete servlet registration, and it is
159             //not clear from servlet 3.0 spec whether this is intended, or if only a ServletContext.addServlet() call
160             //can complete it, see http://java.net/jira/browse/SERVLET_SPEC-42
161             if (holder.getClassName() == null)
162                 holder.setClassName(clazz.getName());
163             if (holder.getHeldClass() == null)
164                 holder.setHeldClass(clazz);
165 
166             //check if the existing servlet has each init-param from the annotation
167             //if not, add it
168             for (WebInitParam ip:annotation.initParams())
169             {
170                 if (metaData.getOrigin(servletName+".servlet.init-param."+ip.name())==Origin.NotSet)
171                 {
172                     holder.setInitParameter(ip.name(), ip.value());
173                     metaData.setOrigin(servletName+".servlet.init-param."+ip.name());
174                 }
175             }
176 
177             //check the url-patterns
178             //ServletSpec 3.0 p81 If a servlet already has url mappings from a
179             //webxml or fragment descriptor the annotation is ignored. However, we want to be able to
180             //replace mappings that were given in webdefault.xml
181             boolean mappingsExist = false;
182             boolean anyNonDefaults = false;
183             ServletMapping[] allMappings = _context.getServletHandler().getServletMappings();
184             if (allMappings != null)
185             {
186                 for (ServletMapping m:allMappings)
187                 {
188                     if (m.getServletName() != null && servletName.equals(m.getServletName()))
189                     {
190                         mappingsExist = true;
191                         if (!m.isDefault())
192                         {
193                             anyNonDefaults = true;
194                             break;
195                         }
196                     }
197                 }
198             }
199 
200             if (anyNonDefaults)
201                 return;  //if any mappings already set by a descriptor that is not webdefault.xml, we're done
202 
203             boolean clash = false;
204             if (mappingsExist)
205             {
206                 for (String p:urlPatternList)
207                 {
208                     ServletMapping m = _context.getServletHandler().getServletMapping(p);
209                     if (m != null && !m.isDefault())
210                     {
211                         //trying to override a servlet-mapping that was added not by webdefault.xml
212                         clash = true;
213                         break;
214                     }
215                 }
216             }
217 
218             if (!mappingsExist || !clash)
219             {
220                 ServletMapping m = new ServletMapping();
221                 m.setServletName(servletName);
222                 m.setPathSpecs(LazyList.toStringArray(urlPatternList));
223                 _context.getServletHandler().addServletMapping(m);
224             }
225         }
226     }
227 }