1 package gboat2.base.plugin.struts.dispatcher;
2
3 import gboat2.base.bridge.GboatAppConstants;
4 import gboat2.base.bridge.GboatAppContext;
5 import gboat2.base.bridge.debug.DefaultDebugHook;
6 import gboat2.base.bridge.exception.DefaultGboatNestedException;
7 import gboat2.base.bridge.util.BundleUtil;
8
9 import java.io.File;
10 import java.io.IOException;
11 import java.net.URL;
12
13 import javax.servlet.http.HttpServletRequest;
14 import javax.servlet.http.HttpServletResponse;
15
16 import org.apache.commons.io.FileUtils;
17 import org.apache.commons.lang3.StringUtils;
18 import org.apache.struts2.ServletActionContext;
19 import org.apache.struts2.dispatcher.ServletDispatcherResult;
20 import org.apache.struts2.dispatcher.StrutsResultSupport;
21 import org.osgi.framework.Bundle;
22 import org.osgi.framework.FrameworkUtil;
23 import org.springframework.util.Assert;
24
25 import com.opensymphony.xwork2.ActionInvocation;
26 import com.opensymphony.xwork2.inject.Container;
27 import com.opensymphony.xwork2.inject.Inject;
28 import com.opensymphony.xwork2.util.TextParseUtil;
29 import com.opensymphony.xwork2.util.logging.Logger;
30 import com.opensymphony.xwork2.util.logging.LoggerFactory;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class GboatDispatcherResult extends ServletDispatcherResult {
64 public static final String STRUTS_GBOAT2_OSGI_RESOURCE_OUTDIR = "struts.gboat2.osgi.resource.outdir";
65 public static final String STRUTS_GBOAT2_OSGI_RESOURCE_DENYSUFFIXS = "struts.gboat2.osgi.resource.denysuffixs";
66
67 protected Container container;
68 protected static String osgiResourceOutDir;
69 protected String[] osgiResourceDenySuffixs;
70
71 private static final long serialVersionUID = 1L;
72
73 private static final Logger LOG = LoggerFactory.getLogger(GboatDispatcherResult.class);
74
75 public GboatDispatcherResult() {
76 super();
77 }
78
79 public GboatDispatcherResult(String location) {
80 super(location);
81 }
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 @Override
100 public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
101 HttpServletRequest request = ServletActionContext.getRequest();
102 HttpServletResponse response = ServletActionContext.getResponse();
103
104 if(StringUtils.endsWithAny(finalLocation, osgiResourceDenySuffixs)) {
105 response.sendError(HttpServletResponse.SC_FORBIDDEN, "result '" + finalLocation + "' is forbidden");
106 return;
107 }
108
109 Object includeServletPath = request.getAttribute(GboatAppConstants.INCLUDE_SERVLET_PATH_KEY);
110 if(includeServletPath == null) {
111
112
113
114
115
116 request.setAttribute(GboatAppConstants.INCLUDE_SERVLET_PATH_KEY, finalLocation);
117 }
118
119 Object action = invocation.getAction();
120 Bundle bundle = FrameworkUtil.getBundle(action.getClass());
121 Assert.notNull(bundle, "没有任何 Bundle 中包含请求的 Action[" + action + "] -> [" + request.getRequestURL() + "]");
122
123
124
125 String targetLocation = processFinalLocation(finalLocation, bundle);
126 extractBundleJsp(finalLocation, bundle);
127
128 super.doExecute(targetLocation, invocation);
129 }
130
131
132
133
134
135
136 public static String getBundleResourcePrefix(Bundle bundle) {
137 String prefix = StringUtils.defaultString(osgiResourceOutDir)
138 + "/" + bundle.getSymbolicName()
139 + "/";
140 return prefix.replace('\\', '/').replaceAll("/+", "/");
141 }
142
143
144
145
146
147
148
149 public static String processFinalLocation(String finalLocation, Bundle bundle) {
150 finalLocation = finalLocation.replace('\\', '/').replaceAll("/+", "/");
151 String prefix = getBundleResourcePrefix(bundle);
152
153 return finalLocation.startsWith(prefix) ? finalLocation : (prefix + StringUtils.removeStart(finalLocation, "/"));
154 }
155
156
157
158
159
160
161 public static String extractBundleJsp(String page) {
162 String innerPath = StringUtils.removeStart(page, osgiResourceOutDir);
163 Bundle bundle = BundleUtil.getBundleForRequestJsp(innerPath, ServletActionContext.getContext().getActionInvocation());
164
165 if(bundle == null) {
166 throw new DefaultGboatNestedException("无法根据 JSP 文件路径[" + page + "] 找到匹配的 Bundle");
167 } else {
168 String symbolicName = bundle.getSymbolicName();
169 if(innerPath.startsWith(symbolicName)) {
170 innerPath = innerPath.substring(symbolicName.length());
171 } else if(innerPath.startsWith("/" + symbolicName)) {
172 innerPath = innerPath.substring(symbolicName.length() + 1);
173 }
174 return extractBundleJsp(innerPath, bundle);
175 }
176 }
177
178
179
180
181
182
183
184 public static String extractBundleJsp(String finalLocation, Bundle bundle){
185 String symbolicName = bundle.getSymbolicName();
186 String targetLocation = processFinalLocation(finalLocation, bundle);
187
188 File targetFile = new File(GboatAppContext.getWebRootPath(), targetLocation);
189
190 DefaultDebugHook debugHook = DefaultDebugHook.getInstance();
191 boolean isDevMode = debugHook.isBundleDebugEnabled(symbolicName);
192 if (isDevMode || (!targetFile.exists())) {
193 File targetDir = targetFile.getParentFile();
194 if(!targetDir.exists() && !targetDir.mkdirs())
195 throw new DefaultGboatNestedException("无法创建输出目录 [" + targetDir.getAbsolutePath() + "]");
196
197 try {
198 if(isDevMode) {
199 File sourceFile = new File(debugHook.getSourceFilePath(symbolicName, finalLocation));
200 if (!sourceFile.exists() || sourceFile.isDirectory()) {
201
202 throw new DefaultGboatNestedException("Bundle[" + symbolicName + "] 开启了调试模式,在其源代码目录下并未发现文件 '"
203 + finalLocation + "' -> [" + sourceFile.getAbsolutePath() + "]");
204 }
205
206 if(!targetFile.exists() || sourceFile.lastModified() > targetFile.lastModified()) {
207 FileUtils.copyFile(sourceFile, targetFile, true);
208 }
209 } else {
210 URL url = bundle.getResource(finalLocation);
211 if (url == null) {
212 LOG.error("Bundle[#0] 中没有找到资源文件 [#1]", symbolicName, finalLocation);
213 } else {
214 FileUtils.copyURLToFile(url, targetFile);
215 }
216 }
217 } catch (IOException e) {
218 throw new DefaultGboatNestedException("从 Bundle[" + symbolicName + "] 中复制文件 [" + finalLocation + "] 到 WebRoot 下发生错误!", e);
219 }
220 }
221 return targetLocation;
222 }
223
224 @Inject
225 public void setContainer(Container container) {
226 this.container = container;
227 }
228
229 @Inject(STRUTS_GBOAT2_OSGI_RESOURCE_OUTDIR)
230 public void setOsgiResourceOutDir(String osgiResourceOutDir) {
231 GboatDispatcherResult.osgiResourceOutDir = osgiResourceOutDir;
232 }
233
234 @Inject(STRUTS_GBOAT2_OSGI_RESOURCE_DENYSUFFIXS)
235 public void setOsgiResourceDenySuffixs(String osgiResourceDenySuffixs) {
236 if(StringUtils.isNotBlank(osgiResourceDenySuffixs)){
237 this.osgiResourceDenySuffixs = TextParseUtil.commaDelimitedStringToSet(osgiResourceDenySuffixs).toArray(new String[0]);
238 }
239 }
240 }