1 package org.apache.turbine;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.PrintWriter;
26 import java.nio.file.Path;
27 import java.nio.file.Paths;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.Map;
31
32 import javax.servlet.ServletConfig;
33 import javax.servlet.ServletContext;
34 import javax.servlet.ServletException;
35 import javax.servlet.annotation.MultipartConfig;
36 import javax.servlet.annotation.WebInitParam;
37 import javax.servlet.annotation.WebServlet;
38 import javax.servlet.http.HttpServlet;
39 import javax.servlet.http.HttpServletRequest;
40 import javax.servlet.http.HttpServletResponse;
41 import javax.xml.bind.JAXBContext;
42 import javax.xml.bind.Unmarshaller;
43
44 import org.apache.commons.configuration2.Configuration;
45 import org.apache.commons.configuration2.PropertiesConfiguration;
46 import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
47 import org.apache.commons.configuration2.builder.combined.CombinedConfigurationBuilder;
48 import org.apache.commons.configuration2.builder.fluent.Parameters;
49 import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
50 import org.apache.commons.configuration2.ex.ConfigurationException;
51 import org.apache.commons.configuration2.io.HomeDirectoryLocationStrategy;
52 import org.apache.commons.lang3.NotImplementedException;
53 import org.apache.commons.lang3.StringUtils;
54 import org.apache.commons.lang3.exception.ExceptionUtils;
55 import org.apache.logging.log4j.LogManager;
56 import org.apache.logging.log4j.Logger;
57 import org.apache.logging.log4j.core.LoggerContext;
58 import org.apache.turbine.modules.PageLoader;
59 import org.apache.turbine.pipeline.Pipeline;
60 import org.apache.turbine.pipeline.PipelineData;
61 import org.apache.turbine.pipeline.TurbinePipeline;
62 import org.apache.turbine.services.Initable;
63 import org.apache.turbine.services.InitializationException;
64 import org.apache.turbine.services.ServiceManager;
65 import org.apache.turbine.services.TurbineServices;
66 import org.apache.turbine.services.rundata.RunDataService;
67 import org.apache.turbine.services.template.TemplateService;
68 import org.apache.turbine.util.LocaleUtils;
69 import org.apache.turbine.util.RunData;
70 import org.apache.turbine.util.ServerData;
71 import org.apache.turbine.util.TurbineConfig;
72 import org.apache.turbine.util.TurbineException;
73 import org.apache.turbine.util.uri.URIConstants;
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114 @WebServlet(name = "Turbine", urlPatterns = { "/app" }, loadOnStartup = 1, initParams = {
115 @WebInitParam(name = TurbineConstants.APPLICATION_ROOT_KEY, value = TurbineConstants.APPLICATION_ROOT_DEFAULT),
116 @WebInitParam(name = TurbineConfig.PROPERTIES_PATH_KEY, value = TurbineConfig.PROPERTIES_PATH_DEFAULT) })
117 @MultipartConfig
118 public class Turbine extends HttpServlet
119 {
120
121 private static final long serialVersionUID = -6317118078613623990L;
122
123
124
125
126
127
128
129 @Deprecated
130 public static final String REDIRECTED_PATHINFO_NAME = "redirected";
131
132
133
134
135 @Deprecated
136 public static final String BASEDIR_KEY = "basedir";
137
138
139
140
141
142
143 private static boolean firstInit = true;
144
145
146
147
148 private static Pipeline pipeline = null;
149
150
151 private static Throwable initFailure = null;
152
153
154
155
156 private static boolean firstDoGet = true;
157
158
159
160
161 private static volatile ServerData serverData = null;
162
163
164 private static String applicationRoot;
165
166
167 private static ServletConfig servletConfig;
168
169
170 private static ServletContext servletContext;
171
172
173
174
175
176 private static String webappRoot;
177
178
179 private static Configuration configuration = null;
180
181
182 private enum ConfigurationStyle
183 {
184 XML, PROPERTIES, JSON, YAML, UNSET
185 }
186
187 private static final Logger log = LogManager.getLogger(Turbine.class);
188
189
190
191
192
193
194
195
196
197 @Override
198 public void init() throws ServletException
199 {
200 synchronized (Turbine.class)
201 {
202 super.init();
203
204 if (!firstInit)
205 {
206 log.info("Double initialization of Turbine was attempted!");
207 return;
208 }
209
210
211 firstInit = false;
212 ServletConfig config = getServletConfig();
213
214 try
215 {
216 ServletContext context = config.getServletContext();
217
218 configure(config, context);
219
220 TemplateServicee/turbine/services/template/TemplateService.html#TemplateService">TemplateService templateService = (TemplateService) getServiceManager().getService(TemplateService.SERVICE_NAME);
221 if (templateService == null)
222 {
223 throw new TurbineException("No Template Service configured!");
224 }
225
226 if (getRunDataService() == null)
227 {
228 throw new TurbineException("No RunData Service configured!");
229 }
230 }
231 catch (Throwable e)
232 {
233
234 initFailure = e;
235 log.fatal("Turbine: init() failed", e);
236 throw new ServletException("Turbine: init() failed", e);
237 }
238
239 log.info("Turbine: init() Ready to Rumble!");
240 }
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257 protected void configure(ServletConfig config, ServletContext context)
258 throws Exception
259 {
260
261
262
263 applicationRoot = findInitParameter(context, config,
264 TurbineConstants.APPLICATION_ROOT_KEY,
265 TurbineConstants.APPLICATION_ROOT_DEFAULT);
266
267 webappRoot = context.getRealPath("/");
268
269
270
271 if (applicationRoot == null || applicationRoot.equals(TurbineConstants.WEB_CONTEXT))
272 {
273 applicationRoot = webappRoot;
274
275
276 }
277
278
279 setApplicationRoot(applicationRoot);
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309 Path confPath = configureApplication(config, context);
310
311 configureLogging(confPath);
312
313
314
315
316 setTurbineServletConfig(config);
317 setTurbineServletContext(context);
318
319 getServiceManager().setApplicationRoot(applicationRoot);
320
321
322
323
324
325
326 configuration.setProperty(TurbineConstants.APPLICATION_ROOT_KEY, applicationRoot);
327 configuration.setProperty(TurbineConstants.WEBAPP_ROOT_KEY, webappRoot);
328
329 getServiceManager().setConfiguration(configuration);
330
331
332
333
334
335 getServiceManager().init();
336
337
338
339 String descriptorPath = configuration.getString(
340 "pipeline.default.descriptor",
341 TurbinePipeline.CLASSIC_PIPELINE);
342
343 log.debug("Using descriptor path: {}", descriptorPath);
344
345
346
347 if (!descriptorPath.startsWith("/"))
348 {
349 descriptorPath = "/" + descriptorPath;
350 }
351
352 try (InputStream reader = context.getResourceAsStream(descriptorPath))
353 {
354 JAXBContext jaxb = JAXBContext.newInstance(TurbinePipeline.class);
355 Unmarshaller unmarshaller = jaxb.createUnmarshaller();
356 pipeline = (Pipeline) unmarshaller.unmarshal(reader);
357 }
358
359 log.debug("Initializing pipeline");
360
361 pipeline.initialize();
362 }
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379 protected Path configureApplication(ServletConfig config, ServletContext context)
380 throws IOException, ConfigurationException
381 {
382 ConfigurationStyle confStyle = ConfigurationStyle.UNSET;
383
384 String confFile = findInitParameter(context, config,
385 TurbineConfig.CONFIGURATION_PATH_KEY,
386 null);
387 if (StringUtils.isNotEmpty(confFile))
388 {
389 confStyle = ConfigurationStyle.XML;
390 }
391 else
392 {
393 confFile = findInitParameter(context, config,
394 TurbineConfig.PROPERTIES_PATH_KEY,
395 null);
396 if (StringUtils.isNotEmpty(confFile))
397 {
398 confStyle = ConfigurationStyle.PROPERTIES;
399 }
400 }
401
402
403 if (confStyle == ConfigurationStyle.UNSET)
404 {
405 confFile = findInitParameter(context, config,
406 TurbineConfig.PROPERTIES_PATH_KEY,
407 TurbineConfig.PROPERTIES_PATH_DEFAULT);
408 confStyle = ConfigurationStyle.PROPERTIES;
409 }
410
411
412 log.debug("Loading configuration ({}) from {}", confStyle, confFile);
413
414
415 Parameters params = new Parameters();
416 File confPath = getApplicationRootAsFile();
417
418 if (confFile.startsWith("/"))
419 {
420 confFile = confFile.substring(1);
421
422
423 }
424
425 Path confFileRelativePath = Paths.get(confFile);
426
427 Path targetPath = Paths.get(confPath.toURI());
428 targetPath = targetPath.resolve(confFileRelativePath);
429
430
431 Path targetPathDirectory = targetPath.getParent();
432 if (targetPathDirectory != null)
433 {
434
435 confPath = targetPathDirectory.normalize().toFile();
436
437 Path targetFilePath = targetPath.getFileName();
438 if (targetFilePath != null)
439 {
440
441 confFile = targetFilePath.toString();
442 }
443
444 }
445
446 switch (confStyle)
447 {
448 case XML:
449
450
451 CombinedConfigurationBuilder combinedBuilder = new CombinedConfigurationBuilder()
452 .configure(params.fileBased()
453 .setFileName(confFile)
454 .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
455 .setLocationStrategy(new HomeDirectoryLocationStrategy(confPath.getCanonicalPath(), false)));
456 configuration = combinedBuilder.getConfiguration();
457 break;
458
459 case PROPERTIES:
460 FileBasedConfigurationBuilder<PropertiesConfiguration> propertiesBuilder = new FileBasedConfigurationBuilder<>(
461 PropertiesConfiguration.class)
462 .configure(params.properties()
463 .setFileName(confFile)
464 .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
465 .setLocationStrategy(new HomeDirectoryLocationStrategy(confPath.getCanonicalPath(), false)));
466 configuration = propertiesBuilder.getConfiguration();
467 break;
468 case JSON:
469 case YAML:
470 throw new NotImplementedException("JSON or XAML configuration style not yet implemented!");
471
472 default:
473 break;
474 }
475
476 log.info("Loaded configuration ({}) from {} style: {}",
477 confStyle, confFile, configuration.toString());
478
479 return targetPath;
480 }
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497 protected String findInitParameter(ServletContext context,
498 ServletConfig config, String name, String defaultValue)
499 {
500 String path = null;
501 String parameterName = name;
502
503
504 boolean usingNamespace = parameterName.startsWith(TurbineConstants.CONFIG_NAMESPACE);
505 while (true)
506 {
507 path = config.getInitParameter(parameterName);
508 if (StringUtils.isEmpty(path))
509 {
510 path = context.getInitParameter(parameterName);
511 if (StringUtils.isEmpty(path))
512 {
513
514 if (usingNamespace)
515 {
516 path = defaultValue;
517 }
518 else
519 {
520
521 parameterName = TurbineConstants.CONFIG_NAMESPACE + '.' + parameterName;
522 usingNamespace = true;
523 continue;
524 }
525 }
526 }
527 break;
528 }
529
530 return path;
531 }
532
533
534
535
536
537
538
539
540 public void init(PipelineData data)
541 {
542 synchronized (Turbine.class)
543 {
544 if (firstDoGet)
545 {
546
547
548
549
550
551 saveServletInfo(data);
552
553
554 TurbineServicesg/apache/turbine/services/TurbineServices.html#TurbineServices">TurbineServices services = (TurbineServices) getServiceManager();
555
556 for (Iterator<String> i = services.getServiceNames(); i.hasNext();)
557 {
558 String serviceName = i.next();
559 Object service = services.getService(serviceName);
560
561 if (service instanceof Initable)
562 {
563 try
564 {
565 ((Initable) service).init(data);
566 }
567 catch (InitializationException e)
568 {
569 log.warn("Could not initialize Initable {} with PipelineData", serviceName, e);
570 }
571 }
572 }
573
574
575 firstDoGet = false;
576 log.info("Turbine: first Request successful");
577 }
578 }
579 }
580
581
582
583
584
585
586 public static Configuration getConfiguration()
587 {
588 return configuration;
589 }
590
591
592
593
594
595
596 public static String getServerName()
597 {
598 return getDefaultServerData().getServerName();
599 }
600
601
602
603
604
605
606 public static String getServerScheme()
607 {
608 return getDefaultServerData().getServerScheme();
609 }
610
611
612
613
614
615
616 public static String getServerPort()
617 {
618 return Integer.toString(getDefaultServerData().getServerPort());
619 }
620
621
622
623
624
625
626
627 public static String getScriptName()
628 {
629 return getDefaultServerData().getScriptName();
630 }
631
632
633
634
635
636
637 public static String getContextPath()
638 {
639 return getDefaultServerData().getContextPath();
640 }
641
642
643
644
645
646
647
648
649
650
651
652
653 public static ServerData getDefaultServerData()
654 {
655 if (serverData == null)
656 {
657 String serverName = configuration.getString(TurbineConstants.DEFAULT_SERVER_NAME_KEY);
658 if (serverName == null)
659 {
660 log.error("ServerData Information requested from Turbine before first request!");
661 }
662 else
663 {
664 log.info("ServerData Information retrieved from configuration.");
665 }
666
667 serverData = new ServerData(serverName,
668 configuration.getInt(TurbineConstants.DEFAULT_SERVER_PORT_KEY,
669 URIConstants.HTTP_PORT),
670 configuration.getString(TurbineConstants.DEFAULT_SERVER_SCHEME_KEY,
671 URIConstants.HTTP),
672 configuration.getString(TurbineConstants.DEFAULT_SCRIPT_NAME_KEY),
673 configuration.getString(TurbineConstants.DEFAULT_CONTEXT_PATH_KEY));
674 }
675 return serverData;
676 }
677
678
679
680
681
682
683
684 public static void setTurbineServletConfig(ServletConfig config)
685 {
686 servletConfig = config;
687 }
688
689
690
691
692
693
694 public static ServletConfig getTurbineServletConfig()
695 {
696 return servletConfig;
697 }
698
699
700
701
702
703
704
705 public static void setTurbineServletContext(ServletContext context)
706 {
707 servletContext = context;
708 }
709
710
711
712
713
714
715 public static ServletContext getTurbineServletContext()
716 {
717 return servletContext;
718 }
719
720
721
722
723
724 @Override
725 public void destroy()
726 {
727
728 getServiceManager().shutdownServices();
729
730 firstInit = true;
731 firstDoGet = true;
732 log.info("Turbine: Done shutting down!");
733 }
734
735
736
737
738
739
740
741
742
743
744
745
746
747 @Override
748 public void doGet(HttpServletRequest req, HttpServletResponse res)
749 throws IOException, ServletException
750 {
751
752 if (initFailure != null)
753 {
754 handleHorribleException(res, initFailure);
755 return;
756 }
757
758
759 try (PipelineData pipelineData = getRunDataService().getRunData(req, res, getServletConfig()))
760 {
761 try
762 {
763
764 Map<Class<?>, Object> runDataMap = new HashMap<Class<?>, Object>();
765 runDataMap.put(RunData.class, pipelineData);
766
767 pipelineData.put(RunData.class, runDataMap);
768
769
770
771
772 if (firstDoGet)
773 {
774 init(pipelineData);
775 }
776
777
778
779
780 pipeline.invoke(pipelineData);
781 }
782 catch (Throwable t)
783 {
784 handleException(pipelineData, res, t);
785 }
786 }
787 catch (Throwable t)
788 {
789 handleHorribleException(res, t);
790 }
791 }
792
793
794
795
796
797
798
799
800
801
802
803
804
805 @Override
806 public void doPost(HttpServletRequest req, HttpServletResponse res)
807 throws IOException, ServletException
808 {
809 doGet(req, res);
810 }
811
812
813
814
815
816
817 @Override
818 public String getServletInfo()
819 {
820 return "Turbine Servlet";
821 }
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838 protected void handleException(PipelineData pipelineData, HttpServletResponse res,
839 Throwable t)
840 {
841 RunData"../../../org/apache/turbine/util/RunData.html#RunData">RunData data = (RunData) pipelineData;
842
843 log.error("Turbine.handleException: ", t);
844
845 try
846 {
847
848
849 data.setStackTrace(ExceptionUtils.getStackTrace(t), t);
850
851
852 data.setScreen(configuration.getString(
853 TurbineConstants.SCREEN_ERROR_KEY,
854 TurbineConstants.SCREEN_ERROR_DEFAULT));
855
856
857 if (data.getTemplateInfo() != null)
858 {
859 data.getTemplateInfo()
860 .setScreenTemplate(configuration.getString(
861 TurbineConstants.TEMPLATE_ERROR_KEY,
862 TurbineConstants.TEMPLATE_ERROR_VM));
863 }
864
865
866 data.setAction("");
867
868 PageLoader.getInstance().exec(pipelineData,
869 configuration.getString(TurbineConstants.PAGE_DEFAULT_KEY,
870 TurbineConstants.PAGE_DEFAULT_DEFAULT));
871
872 data.getResponse().setContentType(data.getContentType());
873 data.getResponse().setStatus(data.getStatusCode());
874 }
875
876 catch (Throwable reallyScrewedNow)
877 {
878 handleHorribleException(res, reallyScrewedNow);
879 }
880 }
881
882
883
884
885
886
887
888
889
890 protected void handleHorribleException(HttpServletResponse res, Throwable t)
891 {
892 try
893 {
894 res.setContentType(TurbineConstants.DEFAULT_TEXT_CONTENT_TYPE);
895 res.setStatus(200);
896 PrintWriter writer = res.getWriter();
897 writer.println("Horrible Exception: ");
898 t.printStackTrace(writer);
899 }
900 catch (Exception ignored)
901 {
902
903 }
904
905 log.error(t.getMessage(), t);
906 }
907
908
909
910
911
912
913
914
915 public static synchronized void saveServletInfo(PipelineData data)
916 {
917
918
919
920
921
922
923
924 ServerData requestServerData = data.get(Turbine.class, ServerData.class);
925 serverData = (ServerData) requestServerData.clone();
926 }
927
928
929
930
931
932
933
934
935
936
937 protected void configureLogging(Path logConf) throws IOException
938 {
939 LoggerContext context = (LoggerContext) LogManager.getContext(false);
940
941 if (context.getConfiguration().getConfigurationSource().getLocation() == null)
942 {
943 Path log4jFile = resolveLog4j2(logConf.getParent());
944
945 if (log4jFile != null)
946 {
947 LogManager.getContext(null, false, log4jFile.toUri());
948 }
949 }
950 log.info("resolved log4j2 location: {}", context.getConfiguration().getConfigurationSource().getLocation());
951 }
952
953
954
955
956
957
958
959
960
961
962 protected Path resolveLog4j2(Path logConfPath)
963 {
964 String log4jFile = configuration.getString(TurbineConstants.LOG4J2_CONFIG_FILE,
965 TurbineConstants.LOG4J2_CONFIG_FILE_DEFAULT);
966
967 if (log4jFile.startsWith("/"))
968 {
969 log4jFile = log4jFile.substring(1);
970 }
971 Path log4jTarget = null;
972 if (StringUtils.isNotEmpty(log4jFile) && !log4jFile.equalsIgnoreCase("none"))
973 {
974
975
976
977 if (logConfPath != null)
978 {
979 Path log4jFilePath = Paths.get(log4jFile);
980 Path logFilePath = logConfPath.resolve(log4jFilePath);
981 if (logFilePath != null && logFilePath.toFile().exists())
982 {
983 log4jTarget = logFilePath.normalize();
984 }
985 else
986 {
987
988 if (log4jFilePath != null && log4jFilePath.getParent() != null && logConfPath.endsWith(log4jFilePath.getParent()))
989 {
990 logFilePath = logConfPath.resolve(log4jFilePath.getFileName());
991 if (logFilePath != null && logFilePath.toFile().exists())
992 {
993 log4jTarget = logFilePath.normalize();
994 }
995 }
996 }
997 }
998 }
999 return log4jTarget;
1000 }
1001
1002
1003
1004
1005
1006
1007
1008 public static void setApplicationRoot(String val)
1009 {
1010 applicationRoot = val;
1011 }
1012
1013
1014
1015
1016
1017
1018 public static String getApplicationRoot()
1019 {
1020 return applicationRoot;
1021 }
1022
1023
1024
1025
1026
1027
1028 public static File getApplicationRootAsFile()
1029 {
1030 return new File(applicationRoot);
1031 }
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041 public static String getRealPath(String path)
1042 {
1043 if (path.startsWith("/"))
1044 {
1045 return new File(getApplicationRootAsFile(), path.substring(1)).getAbsolutePath();
1046 }
1047
1048 return new File(getApplicationRootAsFile(), path).getAbsolutePath();
1049 }
1050
1051
1052
1053
1054
1055
1056 private ServiceManager getServiceManager()
1057 {
1058 return TurbineServices.getInstance();
1059 }
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070 @Deprecated
1071 public static String getDefaultInputEncoding()
1072 {
1073 return LocaleUtils.getDefaultInputEncoding();
1074 }
1075
1076
1077
1078
1079
1080
1081 private RunDataService getRunDataService()
1082 {
1083 return (RunDataService) getServiceManager().getService(RunDataService.SERVICE_NAME);
1084 }
1085 }