View Javadoc

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