SystemHelper.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. /*
  2. * Copyright 2009-2013 the Fess Project and the Others.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  13. * either express or implied. See the License for the specific language
  14. * governing permissions and limitations under the License.
  15. */
  16. package jp.sf.fess.helper;
  17. import java.io.File;
  18. import java.io.FilenameFilter;
  19. import java.io.Serializable;
  20. import java.io.UnsupportedEncodingException;
  21. import java.net.URLEncoder;
  22. import java.sql.Timestamp;
  23. import java.util.ArrayList;
  24. import java.util.HashMap;
  25. import java.util.List;
  26. import java.util.Locale;
  27. import java.util.Map;
  28. import java.util.Set;
  29. import java.util.concurrent.ConcurrentHashMap;
  30. import java.util.concurrent.atomic.AtomicBoolean;
  31. import javax.servlet.ServletContext;
  32. import jp.sf.fess.Constants;
  33. import jp.sf.fess.FessSystemException;
  34. import jp.sf.fess.db.exentity.RoleType;
  35. import jp.sf.fess.exec.Crawler;
  36. import jp.sf.fess.job.JobExecutor;
  37. import jp.sf.fess.service.RoleTypeService;
  38. import jp.sf.fess.util.InputStreamThread;
  39. import jp.sf.fess.util.ResourceUtil;
  40. import org.apache.commons.io.IOUtils;
  41. import org.apache.commons.lang.StringUtils;
  42. import org.apache.commons.lang.SystemUtils;
  43. import org.codelibs.solr.lib.SolrGroup;
  44. import org.codelibs.solr.lib.policy.QueryType;
  45. import org.codelibs.solr.lib.policy.StatusPolicy;
  46. import org.seasar.framework.container.SingletonS2Container;
  47. import org.seasar.framework.container.annotation.tiger.DestroyMethod;
  48. import org.seasar.framework.container.annotation.tiger.InitMethod;
  49. import org.seasar.framework.util.FileUtil;
  50. import org.seasar.framework.util.StringUtil;
  51. import org.seasar.robot.util.CharUtil;
  52. import org.seasar.struts.util.RequestUtil;
  53. import org.seasar.struts.util.ServletContextUtil;
  54. import org.slf4j.Logger;
  55. import org.slf4j.LoggerFactory;
  56. public class SystemHelper implements Serializable {
  57. private static final long serialVersionUID = 1L;
  58. private static final Logger logger = LoggerFactory
  59. .getLogger(SystemHelper.class);
  60. private final ConcurrentHashMap<String, Process> runningProcessMap = new ConcurrentHashMap<String, Process>();
  61. private final ConcurrentHashMap<Long, JobExecutor> runningJobExecutorMap = new ConcurrentHashMap<Long, JobExecutor>();
  62. private String adminRole = "fess";
  63. private String[] crawlerJavaOptions = new String[] {
  64. "-Djava.awt.headless=true", "-server", "-Xmx512m",
  65. "-XX:MaxPermSize=128m", "-XX:-UseGCOverheadLimit",
  66. "-XX:+UseConcMarkSweepGC", "-XX:CMSInitiatingOccupancyFraction=75",
  67. "-XX:+CMSIncrementalMode", "-XX:+CMSIncrementalPacing",
  68. "-XX:CMSIncrementalDutyCycleMin=0", "-XX:+UseParNewGC",
  69. "-XX:+UseStringCache", "-XX:+UseTLAB", "-XX:+DisableExplicitGC" };
  70. private String logFilePath = System.getProperty("fess.log.file");
  71. private String solrHome = System.getProperty("solr.solr.home");
  72. private String solrDataDirName = "fess.solr.data.dir";
  73. private String javaCommandPath = "java";
  74. private String filterPathEncoding = Constants.UTF_8;
  75. private boolean useOwnTmpDir = true;
  76. private String baseHelpLink = "http://fess.codelibs.org/{lang}/"
  77. + Constants.MAJOR_VERSION + ".0/admin/";
  78. private String[] supportedHelpLangs = new String[] { "ja" };
  79. private final Map<String, String> designJspFileNameMap = new HashMap<String, String>();
  80. private String[] supportedUploadedJSExtentions = new String[] { "js" };
  81. private String[] supportedUploadedCssExtentions = new String[] { "css" };
  82. private String[] supportedUploadedMediaExtentions = new String[] { "jpg",
  83. "jpeg", "gif", "png", "swf" };
  84. private String jarDir = "/jar/";
  85. private String launcherFileNamePrefix = "fess-launcher-";
  86. private String launcherJarPath;
  87. private String launcherJnlpPath;
  88. private final AtomicBoolean forceStop = new AtomicBoolean(false);
  89. @InitMethod
  90. public void init() {
  91. final File[] files = ResourceUtil.getJarFiles(launcherFileNamePrefix);
  92. if (files.length > 0) {
  93. final String fileName = files[0].getName();
  94. final String jarPath = ServletContextUtil.getServletContext()
  95. .getRealPath(jarDir);
  96. final File[] jarFiles = new File(jarPath)
  97. .listFiles(new FilenameFilter() {
  98. @Override
  99. public boolean accept(final File dir, final String name) {
  100. return name.startsWith(launcherFileNamePrefix);
  101. }
  102. });
  103. if (jarFiles != null) {
  104. for (final File jarFile : jarFiles) {
  105. if (jarFile.exists() && !jarFile.delete()) {
  106. logger.warn("Could not delete "
  107. + jarFile.getAbsolutePath());
  108. }
  109. }
  110. }
  111. final File launcherJarFile = new File(jarPath, fileName);
  112. final File parentLauncherJarFile = launcherJarFile.getParentFile();
  113. if (!parentLauncherJarFile.exists()
  114. && !parentLauncherJarFile.mkdirs()) {
  115. logger.warn("Could not create "
  116. + parentLauncherJarFile.getAbsolutePath());
  117. }
  118. FileUtil.copy(files[0], launcherJarFile);
  119. launcherJarPath = jarDir + fileName;
  120. launcherJnlpPath = launcherJarPath.replace(".jar", ".jnlp");
  121. final File launcherJnlpFile = new File(launcherJarFile
  122. .getAbsolutePath().replace(".jar", ".jnlp"));
  123. final File jnlpTemplateFile = new File(
  124. ResourceUtil.getOrigPath("jnlp/fess-launcher.jnlp"));
  125. if (!jnlpTemplateFile.isFile()) {
  126. throw new FessSystemException(
  127. jnlpTemplateFile.getAbsolutePath() + " is not found.");
  128. }
  129. try {
  130. String content = new String(
  131. FileUtil.getBytes(jnlpTemplateFile), Constants.UTF_8);
  132. content = content.replace("${launcherJarFile}", fileName);
  133. FileUtil.write(launcherJnlpFile.getAbsolutePath(),
  134. content.getBytes(Constants.UTF_8));
  135. } catch (final UnsupportedEncodingException e) {
  136. throw new FessSystemException("Could not write "
  137. + jnlpTemplateFile.getAbsolutePath(), e);
  138. }
  139. }
  140. }
  141. @DestroyMethod
  142. public void destroy() {
  143. for (final String sessionId : runningProcessMap.keySet()) {
  144. destroyCrawlerProcess(sessionId);
  145. }
  146. }
  147. public void executeCrawler(final String sessionId,
  148. final String[] webConfigIds, final String[] fileConfigIds,
  149. final String[] dataConfigIds, final String operation) {
  150. final List<String> crawlerCmdList = new ArrayList<String>();
  151. final String cpSeparator = SystemUtils.IS_OS_WINDOWS ? ";" : ":";
  152. final ServletContext servletContext = SingletonS2Container
  153. .getComponent(ServletContext.class);
  154. crawlerCmdList.add(javaCommandPath);
  155. // -cp
  156. crawlerCmdList.add("-cp");
  157. final StringBuilder buf = new StringBuilder();
  158. // WEB-INF/cmd/resources
  159. buf.append("WEB-INF");
  160. buf.append(File.separator);
  161. buf.append("cmd");
  162. buf.append(File.separator);
  163. buf.append("resources");
  164. buf.append(cpSeparator);
  165. // WEB-INF/classes
  166. buf.append("WEB-INF");
  167. buf.append(File.separator);
  168. buf.append("classes");
  169. // WEB-INF/lib
  170. appendJarFile(cpSeparator, servletContext, buf, "/WEB-INF/lib",
  171. "WEB-INF" + File.separator + "lib" + File.separator);
  172. // WEB-INF/cmd/lib
  173. appendJarFile(cpSeparator, servletContext, buf, "/WEB-INF/cmd/lib",
  174. "WEB-INF" + File.separator + "cmd" + File.separator + "lib"
  175. + File.separator);
  176. crawlerCmdList.add(buf.toString());
  177. final String solrDataDir = System.getProperty(solrDataDirName);
  178. crawlerCmdList.add("-Dfess.crawler.process=true");
  179. crawlerCmdList.add("-Dsolr.solr.home=" + solrHome);
  180. if (solrDataDir != null) {
  181. crawlerCmdList.add("-Dsolr.data.dir=" + solrDataDir);
  182. } else {
  183. logger.warn("-D" + solrDataDirName + " is not found.");
  184. }
  185. crawlerCmdList.add("-Dfess.log.file=" + logFilePath);
  186. if (crawlerJavaOptions != null) {
  187. for (final String value : crawlerJavaOptions) {
  188. crawlerCmdList.add(value);
  189. }
  190. }
  191. File ownTmpDir = null;
  192. if (useOwnTmpDir) {
  193. final String tmpDir = System.getProperty("java.io.tmpdir");
  194. if (StringUtil.isNotBlank(tmpDir)) {
  195. ownTmpDir = new File(tmpDir, "fessTmpDir_" + sessionId);
  196. if (ownTmpDir.mkdirs()) {
  197. crawlerCmdList.add("-Djava.io.tmpdir="
  198. + ownTmpDir.getAbsolutePath());
  199. } else {
  200. ownTmpDir = null;
  201. }
  202. }
  203. }
  204. crawlerCmdList.add(Crawler.class.getCanonicalName());
  205. crawlerCmdList.add("--sessionId");
  206. crawlerCmdList.add(sessionId);
  207. crawlerCmdList.add("--name");
  208. crawlerCmdList.add(Constants.CRAWLING_SESSION_SYSTEM_NAME);
  209. if (webConfigIds != null && webConfigIds.length > 0) {
  210. crawlerCmdList.add("-w");
  211. crawlerCmdList.add(StringUtils.join(webConfigIds, ','));
  212. }
  213. if (fileConfigIds != null && fileConfigIds.length > 0) {
  214. crawlerCmdList.add("-f");
  215. crawlerCmdList.add(StringUtils.join(fileConfigIds, ','));
  216. }
  217. if (dataConfigIds != null && dataConfigIds.length > 0) {
  218. crawlerCmdList.add("-d");
  219. crawlerCmdList.add(StringUtils.join(dataConfigIds, ','));
  220. }
  221. if (StringUtil.isNotBlank(operation)) {
  222. crawlerCmdList.add("-o");
  223. crawlerCmdList.add(operation);
  224. }
  225. final File baseDir = new File(servletContext.getRealPath("/"));
  226. if (logger.isInfoEnabled()) {
  227. logger.info("Crawler: \nDirectory=" + baseDir + "\nOptions="
  228. + crawlerCmdList);
  229. }
  230. final ProcessBuilder pb = new ProcessBuilder(crawlerCmdList);
  231. pb.directory(baseDir);
  232. pb.redirectErrorStream(true);
  233. destroyCrawlerProcess(sessionId);
  234. try {
  235. final Process currentProcess = pb.start();
  236. destroyCrawlerProcess(runningProcessMap.putIfAbsent(sessionId,
  237. currentProcess));
  238. final InputStreamThread it = new InputStreamThread(
  239. currentProcess.getInputStream(), Constants.UTF_8);
  240. it.start();
  241. currentProcess.waitFor();
  242. it.join(5000);
  243. final int exitValue = currentProcess.exitValue();
  244. if (logger.isInfoEnabled()) {
  245. logger.info("Crawler: Exit Code=" + exitValue
  246. + " - Crawler Process Output:\n" + it.getOutput());
  247. }
  248. if (exitValue != 0) {
  249. throw new FessSystemException("Exit code is " + exitValue
  250. + "\nOutput:\n" + it.getOutput());
  251. }
  252. } catch (final FessSystemException e) {
  253. throw e;
  254. } catch (final InterruptedException e) {
  255. logger.warn("Crawler Process interrupted.");
  256. } catch (final Exception e) {
  257. throw new FessSystemException("Crawler Process terminated.", e);
  258. } finally {
  259. destroyCrawlerProcess(sessionId);
  260. if (ownTmpDir != null && !ownTmpDir.delete()) {
  261. logger.warn("Could not delete a temp dir: "
  262. + ownTmpDir.getAbsolutePath());
  263. }
  264. }
  265. }
  266. public void destroyCrawlerProcess(final String sessionId) {
  267. final Process process = runningProcessMap.remove(sessionId);
  268. destroyCrawlerProcess(process);
  269. }
  270. protected void destroyCrawlerProcess(final Process process) {
  271. if (process != null) {
  272. try {
  273. IOUtils.closeQuietly(process.getInputStream());
  274. } catch (final Exception e) {
  275. }
  276. try {
  277. IOUtils.closeQuietly(process.getErrorStream());
  278. } catch (final Exception e) {
  279. }
  280. try {
  281. IOUtils.closeQuietly(process.getOutputStream());
  282. } catch (final Exception e) {
  283. }
  284. try {
  285. process.destroy();
  286. } catch (final Exception e) {
  287. }
  288. }
  289. }
  290. private void appendJarFile(final String cpSeparator,
  291. final ServletContext servletContext, final StringBuilder buf,
  292. final String libDirPath, final String basePath) {
  293. final File libDir = new File(servletContext.getRealPath(libDirPath));
  294. final File[] jarFiles = libDir.listFiles(new FilenameFilter() {
  295. @Override
  296. public boolean accept(final File dir, final String name) {
  297. return name.toLowerCase().endsWith(".jar");
  298. }
  299. });
  300. if (jarFiles != null) {
  301. for (final File file : jarFiles) {
  302. buf.append(cpSeparator);
  303. buf.append(basePath);
  304. buf.append(file.getName());
  305. }
  306. }
  307. }
  308. public String getUsername() {
  309. String username = RequestUtil.getRequest().getRemoteUser();
  310. if (StringUtil.isBlank(username)) {
  311. username = "guest";
  312. }
  313. return username;
  314. }
  315. public Timestamp getCurrentTimestamp() {
  316. return new Timestamp(System.currentTimeMillis());
  317. }
  318. public boolean isCrawlProcessRunning() {
  319. return !runningProcessMap.isEmpty();
  320. }
  321. public String getLogFilePath() {
  322. return logFilePath;
  323. }
  324. public void setLogFilePath(final String logFilePath) {
  325. this.logFilePath = logFilePath;
  326. }
  327. public String encodeUrlFilter(final String path) {
  328. if (filterPathEncoding == null || path == null) {
  329. return path;
  330. }
  331. try {
  332. final StringBuilder buf = new StringBuilder();
  333. for (int i = 0; i < path.length(); i++) {
  334. final char c = path.charAt(i);
  335. if (CharUtil.isUrlChar(c) || c == '^' || c == '{' || c == '}'
  336. || c == '|' || c == '\\') {
  337. buf.append(c);
  338. } else {
  339. buf.append(URLEncoder.encode(String.valueOf(c),
  340. filterPathEncoding));
  341. }
  342. }
  343. return buf.toString();
  344. } catch (final UnsupportedEncodingException e) {
  345. return path;
  346. }
  347. }
  348. public String getHelpLink(final String name) {
  349. final Locale locale = RequestUtil.getRequest().getLocale();
  350. if (locale != null) {
  351. final String lang = locale.getLanguage();
  352. for (final String l : supportedHelpLangs) {
  353. if (l.equals(lang)) {
  354. final String url = baseHelpLink + name + "-guide.html";
  355. return url.replaceAll("\\{lang\\}", lang);
  356. }
  357. }
  358. }
  359. return null;
  360. }
  361. public void addDesignJspFileName(final String key, final String value) {
  362. designJspFileNameMap.put(key, value);
  363. }
  364. public String getDesignJspFileName(final String fileName) {
  365. return designJspFileNameMap.get(fileName);
  366. }
  367. public String getAdminRole() {
  368. return adminRole;
  369. }
  370. public void setAdminRole(final String adminRole) {
  371. this.adminRole = adminRole;
  372. }
  373. public List<String> getAuthenticatedRoleList() {
  374. final RoleTypeService roleTypeService = SingletonS2Container
  375. .getComponent(RoleTypeService.class);
  376. final List<RoleType> roleTypeList = roleTypeService.getRoleTypeList();
  377. final List<String> roleList = new ArrayList<String>(roleTypeList.size());
  378. for (final RoleType roleType : roleTypeList) {
  379. roleList.add(roleType.getValue());
  380. }
  381. return roleList;
  382. }
  383. public String[] getCrawlerJavaOptions() {
  384. return crawlerJavaOptions;
  385. }
  386. public void setCrawlerJavaOptions(final String[] crawlerJavaOptions) {
  387. this.crawlerJavaOptions = crawlerJavaOptions;
  388. }
  389. public String getSolrHome() {
  390. return solrHome;
  391. }
  392. public void setSolrHome(final String solrHome) {
  393. this.solrHome = solrHome;
  394. }
  395. public String getSolrDataDirName() {
  396. return solrDataDirName;
  397. }
  398. public void setSolrDataDirName(final String solrDataDirName) {
  399. this.solrDataDirName = solrDataDirName;
  400. }
  401. public String getJavaCommandPath() {
  402. return javaCommandPath;
  403. }
  404. public void setJavaCommandPath(final String javaCommandPath) {
  405. this.javaCommandPath = javaCommandPath;
  406. }
  407. /**
  408. * @return the filterPathEncoding
  409. */
  410. public String getFilterPathEncoding() {
  411. return filterPathEncoding;
  412. }
  413. /**
  414. * @param filterPathEncoding the filterPathEncoding to set
  415. */
  416. public void setFilterPathEncoding(final String filterPathEncoding) {
  417. this.filterPathEncoding = filterPathEncoding;
  418. }
  419. /**
  420. * @return the useOwnTmpDir
  421. */
  422. public boolean isUseOwnTmpDir() {
  423. return useOwnTmpDir;
  424. }
  425. /**
  426. * @param useOwnTmpDir the useOwnTmpDir to set
  427. */
  428. public void setUseOwnTmpDir(final boolean useOwnTmpDir) {
  429. this.useOwnTmpDir = useOwnTmpDir;
  430. }
  431. /**
  432. * @return the baseHelpLink
  433. */
  434. public String getBaseHelpLink() {
  435. return baseHelpLink;
  436. }
  437. /**
  438. * @param baseHelpLink the baseHelpLink to set
  439. */
  440. public void setBaseHelpLink(final String baseHelpLink) {
  441. this.baseHelpLink = baseHelpLink;
  442. }
  443. /**
  444. * @return the supportedHelpLangs
  445. */
  446. public String[] getSupportedHelpLangs() {
  447. return supportedHelpLangs;
  448. }
  449. /**
  450. * @param supportedHelpLangs the supportedHelpLangs to set
  451. */
  452. public void setSupportedHelpLangs(final String[] supportedHelpLangs) {
  453. this.supportedHelpLangs = supportedHelpLangs;
  454. }
  455. public String[] getSupportedUploadedJSExtentions() {
  456. return supportedUploadedJSExtentions;
  457. }
  458. public void setSupportedUploadedJSExtentions(
  459. final String[] supportedUploadedJSExtentions) {
  460. this.supportedUploadedJSExtentions = supportedUploadedJSExtentions;
  461. }
  462. public String[] getSupportedUploadedCssExtentions() {
  463. return supportedUploadedCssExtentions;
  464. }
  465. public void setSupportedUploadedCssExtentions(
  466. final String[] supportedUploadedCssExtentions) {
  467. this.supportedUploadedCssExtentions = supportedUploadedCssExtentions;
  468. }
  469. public String[] getSupportedUploadedMediaExtentions() {
  470. return supportedUploadedMediaExtentions;
  471. }
  472. public void setSupportedUploadedMediaExtentions(
  473. final String[] supportedUploadedMediaExtentions) {
  474. this.supportedUploadedMediaExtentions = supportedUploadedMediaExtentions;
  475. }
  476. public String getJarDir() {
  477. return jarDir;
  478. }
  479. public void setJarDir(final String jarDir) {
  480. this.jarDir = jarDir;
  481. }
  482. public String getLauncherFileNamePrefix() {
  483. return launcherFileNamePrefix;
  484. }
  485. public void setLauncherFileNamePrefix(final String launcherFileNamePrefix) {
  486. this.launcherFileNamePrefix = launcherFileNamePrefix;
  487. }
  488. public String getLauncherJarPath() {
  489. return launcherJarPath;
  490. }
  491. public String getLauncherJnlpPath() {
  492. return launcherJnlpPath;
  493. }
  494. public void updateStatus(final SolrGroup solrGroup,
  495. final QueryType queryType) {
  496. final StatusPolicy statusPolicy = solrGroup.getStatusPolicy();
  497. for (final String serverName : solrGroup.getServerNames()) {
  498. statusPolicy.activate(queryType, serverName);
  499. }
  500. }
  501. public Set<String> getRunningSessionIdSet() {
  502. return runningProcessMap.keySet();
  503. }
  504. public boolean isForceStop() {
  505. return forceStop.get();
  506. }
  507. public void setForceStop(final boolean b) {
  508. forceStop.set(true);
  509. }
  510. public void addRunningJobExecutoer(final Long id,
  511. final JobExecutor jobExecutor) {
  512. runningJobExecutorMap.put(id, jobExecutor);
  513. }
  514. public void removeRunningJobExecutoer(final Long id) {
  515. runningJobExecutorMap.remove(id);
  516. }
  517. public boolean isRunningJobExecutoer(final Long id) {
  518. return runningJobExecutorMap.containsKey(id);
  519. }
  520. }