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
380
381 protected Path configureApplication(ServletConfig config, ServletContext context)
382 throws IOException, ConfigurationException
383 {
384 ConfigurationStyle confStyle = ConfigurationStyle.UNSET;
385
386 String confFile = findInitParameter(context, config,
387 TurbineConfig.CONFIGURATION_PATH_KEY,
388 null);
389 if (StringUtils.isNotEmpty(confFile))
390 {
391 confStyle = ConfigurationStyle.XML;
392 }
393 else
394 {
395 confFile = findInitParameter(context, config,
396 TurbineConfig.PROPERTIES_PATH_KEY,
397 null);
398 if (StringUtils.isNotEmpty(confFile))
399 {
400 confStyle = ConfigurationStyle.PROPERTIES;
401 }
402 }
403
404
405 if (confStyle == ConfigurationStyle.UNSET)
406 {
407 confFile = findInitParameter(context, config,
408 TurbineConfig.PROPERTIES_PATH_KEY,
409 TurbineConfig.PROPERTIES_PATH_DEFAULT);
410 confStyle = ConfigurationStyle.PROPERTIES;
411 }
412
413
414 log.debug("Loading configuration ({}) from {}", confStyle, confFile);
415
416
417 Parameters params = new Parameters();
418 File confPath = getApplicationRootAsFile();
419
420 if (confFile.startsWith("/"))
421 {
422 confFile = confFile.substring(1);
423
424
425 }
426
427 Path confFileRelativePath = Paths.get(confFile);
428
429 Path targetPath = Paths.get(confPath.toURI());
430 targetPath = targetPath.resolve(confFileRelativePath);
431
432
433 Path targetPathDirectory = targetPath.getParent();
434 if (targetPathDirectory != null)
435 {
436
437 confPath = targetPathDirectory.normalize().toFile();
438
439 Path targetFilePath = targetPath.getFileName();
440 if (targetFilePath != null)
441 {
442
443 confFile = targetFilePath.toString();
444 }
445
446 }
447
448 switch (confStyle)
449 {
450 case XML:
451
452
453 CombinedConfigurationBuilder combinedBuilder = new CombinedConfigurationBuilder()
454 .configure(params.fileBased()
455 .setFileName(confFile)
456 .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
457 .setLocationStrategy(new HomeDirectoryLocationStrategy(confPath.getCanonicalPath(), false)));
458 configuration = combinedBuilder.getConfiguration();
459 break;
460
461 case PROPERTIES:
462 FileBasedConfigurationBuilder<PropertiesConfiguration> propertiesBuilder = new FileBasedConfigurationBuilder<>(
463 PropertiesConfiguration.class)
464 .configure(params.properties()
465 .setFileName(confFile)
466 .setListDelimiterHandler(new DefaultListDelimiterHandler(','))
467 .setLocationStrategy(new HomeDirectoryLocationStrategy(confPath.getCanonicalPath(), false)));
468
469 configuration = propertiesBuilder.getConfiguration();
470 break;
471 case JSON:
472 case YAML:
473 throw new NotImplementedException("JSON or XAML configuration style not yet implemented!");
474
475 default:
476 break;
477 }
478
479 log.info("Loaded configuration ({}) from {} style: {}",
480 confStyle, confFile, configuration.toString());
481
482 return targetPath;
483 }
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500 protected String findInitParameter(ServletContext context,
501 ServletConfig config, String name, String defaultValue)
502 {
503 String path = null;
504 String parameterName = name;
505
506
507 boolean usingNamespace = parameterName.startsWith(TurbineConstants.CONFIG_NAMESPACE);
508 while (true)
509 {
510 path = config.getInitParameter(parameterName);
511 if (StringUtils.isEmpty(path))
512 {
513 path = context.getInitParameter(parameterName);
514 if (StringUtils.isEmpty(path))
515 {
516
517 if (usingNamespace)
518 {
519 path = defaultValue;
520 }
521 else
522 {
523
524 parameterName = TurbineConstants.CONFIG_NAMESPACE + '.' + parameterName;
525 usingNamespace = true;
526 continue;
527 }
528 }
529 }
530 break;
531 }
532
533 return path;
534 }
535
536
537
538
539
540
541
542
543 public void init(PipelineData data)
544 {
545 synchronized (Turbine.class)
546 {
547 if (firstDoGet)
548 {
549
550
551
552
553
554 saveServletInfo(data);
555
556
557 TurbineServicesg/apache/turbine/services/TurbineServices.html#TurbineServices">TurbineServices services = (TurbineServices) getServiceManager();
558
559 for (Iterator<String> i = services.getServiceNames(); i.hasNext();)
560 {
561 String serviceName = i.next();
562 Object service = services.getService(serviceName);
563
564 if (service instanceof Initable)
565 {
566 try
567 {
568 ((Initable) service).init(data);
569 }
570 catch (InitializationException e)
571 {
572 log.warn("Could not initialize Initable {} with PipelineData", serviceName, e);
573 }
574 }
575 }
576
577
578 firstDoGet = false;
579 log.info("Turbine: first Request successful");
580 }
581 }
582 }
583
584
585
586
587
588
589 public static Configuration getConfiguration()
590 {
591 return configuration;
592 }
593
594
595
596
597
598
599 public static String getServerName()
600 {
601 return getDefaultServerData().getServerName();
602 }
603
604
605
606
607
608
609 public static String getServerScheme()
610 {
611 return getDefaultServerData().getServerScheme();
612 }
613
614
615
616
617
618
619 public static String getServerPort()
620 {
621 return Integer.toString(getDefaultServerData().getServerPort());
622 }
623
624
625
626
627
628
629
630 public static String getScriptName()
631 {
632 return getDefaultServerData().getScriptName();
633 }
634
635
636
637
638
639
640 public static String getContextPath()
641 {
642 return getDefaultServerData().getContextPath();
643 }
644
645
646
647
648
649
650
651
652
653
654
655
656 public static ServerData getDefaultServerData()
657 {
658 if (serverData == null)
659 {
660 String serverName = configuration.getString(TurbineConstants.DEFAULT_SERVER_NAME_KEY);
661 if (serverName == null)
662 {
663 log.error("ServerData Information requested from Turbine before first request!");
664 }
665 else
666 {
667 log.info("ServerData Information retrieved from configuration.");
668 }
669
670 serverData = new ServerData(serverName,
671 configuration.getInt(TurbineConstants.DEFAULT_SERVER_PORT_KEY,
672 URIConstants.HTTP_PORT),
673 configuration.getString(TurbineConstants.DEFAULT_SERVER_SCHEME_KEY,
674 URIConstants.HTTP),
675 configuration.getString(TurbineConstants.DEFAULT_SCRIPT_NAME_KEY),
676 configuration.getString(TurbineConstants.DEFAULT_CONTEXT_PATH_KEY));
677 }
678 return serverData;
679 }
680
681
682
683
684
685
686
687 public static void setTurbineServletConfig(ServletConfig config)
688 {
689 servletConfig = config;
690 }
691
692
693
694
695
696
697 public static ServletConfig getTurbineServletConfig()
698 {
699 return servletConfig;
700 }
701
702
703
704
705
706
707
708 public static void setTurbineServletContext(ServletContext context)
709 {
710 servletContext = context;
711 }
712
713
714
715
716
717
718 public static ServletContext getTurbineServletContext()
719 {
720 return servletContext;
721 }
722
723
724
725
726
727 @Override
728 public void destroy()
729 {
730
731 getServiceManager().shutdownServices();
732
733 firstInit = true;
734 firstDoGet = true;
735 log.info("Turbine: Done shutting down!");
736 }
737
738
739
740
741
742
743
744
745
746
747
748
749
750 @Override
751 public void doGet(HttpServletRequest req, HttpServletResponse res)
752 throws IOException, ServletException
753 {
754
755 if (initFailure != null)
756 {
757 handleHorribleException(res, initFailure);
758 return;
759 }
760
761
762 try (PipelineData pipelineData = getRunDataService().getRunData(req, res, getServletConfig()))
763 {
764 try
765 {
766
767 Map<Class<?>, Object> runDataMap = new HashMap<>();
768 runDataMap.put(RunData.class, pipelineData);
769
770 pipelineData.put(RunData.class, runDataMap);
771
772
773
774
775 if (firstDoGet)
776 {
777 init(pipelineData);
778 }
779
780
781
782
783 pipeline.invoke(pipelineData);
784 }
785 catch (Throwable t)
786 {
787 handleException(pipelineData, res, t);
788 }
789 }
790 catch (Throwable t)
791 {
792 handleHorribleException(res, t);
793 }
794 }
795
796
797
798
799
800
801
802
803
804
805
806
807
808 @Override
809 public void doPost(HttpServletRequest req, HttpServletResponse res)
810 throws IOException, ServletException
811 {
812 doGet(req, res);
813 }
814
815
816
817
818
819
820 @Override
821 public String getServletInfo()
822 {
823 return "Turbine Servlet";
824 }
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841 protected void handleException(PipelineData pipelineData, HttpServletResponse res,
842 Throwable t)
843 {
844 RunData"../../../org/apache/turbine/util/RunData.html#RunData">RunData data = (RunData) pipelineData;
845
846 log.error("Turbine.handleException: ", t);
847
848 try
849 {
850
851
852 data.setStackTrace(ExceptionUtils.getStackTrace(t), t);
853
854
855 data.setScreen(configuration.getString(
856 TurbineConstants.SCREEN_ERROR_KEY,
857 TurbineConstants.SCREEN_ERROR_DEFAULT));
858
859
860 if (data.getTemplateInfo() != null)
861 {
862 data.getTemplateInfo()
863 .setScreenTemplate(configuration.getString(
864 TurbineConstants.TEMPLATE_ERROR_KEY,
865 TurbineConstants.TEMPLATE_ERROR_VM));
866 }
867
868
869 data.setAction("");
870
871 PageLoader.getInstance().exec(pipelineData,
872 configuration.getString(TurbineConstants.PAGE_DEFAULT_KEY,
873 TurbineConstants.PAGE_DEFAULT_DEFAULT));
874
875 data.getResponse().setContentType(data.getContentType());
876 data.getResponse().setStatus(data.getStatusCode());
877 }
878
879 catch (Throwable reallyScrewedNow)
880 {
881 handleHorribleException(res, reallyScrewedNow);
882 }
883 }
884
885
886
887
888
889
890
891
892
893 protected void handleHorribleException(HttpServletResponse res, Throwable t)
894 {
895 try
896 {
897 res.setContentType(TurbineConstants.DEFAULT_TEXT_CONTENT_TYPE);
898 res.setStatus(200);
899 PrintWriter writer = res.getWriter();
900 writer.println("Horrible Exception: ");
901 t.printStackTrace(writer);
902 }
903 catch (Exception ignored)
904 {
905
906 }
907
908 log.error(t.getMessage(), t);
909 }
910
911
912
913
914
915
916
917
918 public static synchronized void saveServletInfo(PipelineData data)
919 {
920
921
922
923
924
925
926
927 ServerData requestServerData = data.get(Turbine.class, ServerData.class);
928 serverData = (ServerData) requestServerData.clone();
929 }
930
931
932
933
934
935
936
937
938
939
940 protected void configureLogging(Path logConf) throws IOException
941 {
942 LoggerContext context = (LoggerContext) LogManager.getContext(false);
943
944 if (context.getConfiguration().getConfigurationSource().getLocation() == null)
945 {
946 Path log4jFile = resolveLog4j2(logConf.getParent());
947
948 if (log4jFile != null)
949 {
950 LogManager.getContext(null, false, log4jFile.toUri());
951 }
952 }
953 log.info("resolved log4j2 location: {}", context.getConfiguration().getConfigurationSource().getLocation());
954 }
955
956
957
958
959
960
961
962
963
964
965 protected Path resolveLog4j2(Path logConfPath)
966 {
967 String log4jFile = configuration.getString(TurbineConstants.LOG4J2_CONFIG_FILE,
968 TurbineConstants.LOG4J2_CONFIG_FILE_DEFAULT);
969
970 if (log4jFile.startsWith("/"))
971 {
972 log4jFile = log4jFile.substring(1);
973 }
974 Path log4jTarget = null;
975 if (StringUtils.isNotEmpty(log4jFile) && !log4jFile.equalsIgnoreCase("none"))
976 {
977
978
979
980 if (logConfPath != null)
981 {
982 Path log4jFilePath = Paths.get(log4jFile);
983 Path logFilePath = logConfPath.resolve(log4jFilePath);
984 if (logFilePath != null && logFilePath.toFile().exists())
985 {
986 log4jTarget = logFilePath.normalize();
987 }
988 else
989 {
990
991 if (log4jFilePath != null && log4jFilePath.getParent() != null && logConfPath.endsWith(log4jFilePath.getParent()))
992 {
993 logFilePath = logConfPath.resolve(log4jFilePath.getFileName());
994 if (logFilePath != null && logFilePath.toFile().exists())
995 {
996 log4jTarget = logFilePath.normalize();
997 }
998 }
999 }
1000 }
1001 }
1002 return log4jTarget;
1003 }
1004
1005
1006
1007
1008
1009
1010
1011 public static void setApplicationRoot(String val)
1012 {
1013 applicationRoot = val;
1014 }
1015
1016
1017
1018
1019
1020
1021 public static String getApplicationRoot()
1022 {
1023 return applicationRoot;
1024 }
1025
1026
1027
1028
1029
1030
1031 public static File getApplicationRootAsFile()
1032 {
1033 return new File(applicationRoot);
1034 }
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044 public static String getRealPath(String path)
1045 {
1046 if (path.startsWith("/"))
1047 {
1048 return new File(getApplicationRootAsFile(), path.substring(1)).getAbsolutePath();
1049 }
1050
1051 return new File(getApplicationRootAsFile(), path).getAbsolutePath();
1052 }
1053
1054
1055
1056
1057
1058
1059 private ServiceManager getServiceManager()
1060 {
1061 return TurbineServices.getInstance();
1062 }
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073 @Deprecated
1074 public static String getDefaultInputEncoding()
1075 {
1076 return LocaleUtils.getDefaultInputEncoding();
1077 }
1078
1079
1080
1081
1082
1083
1084 private RunDataService getRunDataService()
1085 {
1086 return (RunDataService) getServiceManager().getService(RunDataService.SERVICE_NAME);
1087 }
1088 }