View Javadoc
1   /*
2    * Copyright 2012-2021 CodeLibs 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 org.codelibs.fess.app.web.admin.plugin;
17  
18  import java.io.File;
19  import java.io.FileOutputStream;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.stream.Collectors;
28  
29  import org.apache.logging.log4j.LogManager;
30  import org.apache.logging.log4j.Logger;
31  import org.codelibs.core.io.CopyUtil;
32  import org.codelibs.fess.annotation.Secured;
33  import org.codelibs.fess.app.web.base.FessAdminAction;
34  import org.codelibs.fess.helper.PluginHelper;
35  import org.codelibs.fess.helper.PluginHelper.Artifact;
36  import org.codelibs.fess.helper.PluginHelper.ArtifactType;
37  import org.codelibs.fess.util.ComponentUtil;
38  import org.codelibs.fess.util.RenderDataUtil;
39  import org.lastaflute.web.Execute;
40  import org.lastaflute.web.response.HtmlResponse;
41  import org.lastaflute.web.ruts.process.ActionRuntime;
42  import org.lastaflute.web.validation.exception.ValidationErrorException;
43  
44  public class AdminPluginAction extends FessAdminAction {
45  
46      public static final String ROLE = "admin-plugin";
47  
48      private static final Logger logger = LogManager.getLogger(AdminPluginAction.class);
49  
50      private static final String UPLOAD = "upload";
51  
52      @Override
53      protected void setupHtmlData(final ActionRuntime runtime) {
54          super.setupHtmlData(runtime);
55          runtime.registerData("helpLink", systemHelper.getHelpLink(fessConfig.getOnlineHelpNamePlugin()));
56      }
57  
58      @Override
59      protected String getActionRole() {
60          return ROLE;
61      }
62  
63      @Execute
64      @Secured({ ROLE, ROLE + VIEW })
65      public HtmlResponse index() {
66          saveToken();
67          return asListHtml();
68      }
69  
70      @Execute
71      @Secured({ ROLE })
72      public HtmlResponse delete(final DeleteForm form) {
73          validate(form, messages -> {}, () -> asHtml(path_AdminPlugin_AdminPluginJsp));
74          verifyToken(() -> asHtml(path_AdminPlugin_AdminPluginJsp));
75          final Artifact artifact = new Artifact(form.name, form.version, null);
76          deleteArtifact(artifact);
77          saveInfo(messages -> messages.addSuccessDeletePlugin(GLOBAL, artifact.getFileName()));
78          return redirect(getClass());
79      }
80  
81      @Execute
82      @Secured({ ROLE })
83      public HtmlResponse install(final InstallForm form) {
84          validate(form, messages -> {}, () -> asHtml(path_AdminPlugin_AdminPluginInstallpluginJsp));
85          verifyToken(() -> asHtml(path_AdminPlugin_AdminPluginInstallpluginJsp));
86          try {
87              if (UPLOAD.equals(form.id)) {
88                  if (form.jarFile == null) {
89                      throwValidationError(messages -> messages.addErrorsPluginFileIsNotFound(GLOBAL, form.id), this::asListHtml);
90                  }
91                  if (!form.jarFile.getFileName().endsWith(".jar")) {
92                      throwValidationError(messages -> messages.addErrorsFileIsNotSupported(GLOBAL, form.jarFile.getFileName()),
93                              this::asListHtml);
94                  }
95                  final String filename = form.jarFile.getFileName();
96                  final File tempFile = ComponentUtil.getSystemHelper().createTempFile("tmp-adminplugin-", ".jar");
97                  try (final InputStream is = form.jarFile.getInputStream(); final OutputStream os = new FileOutputStream(tempFile)) {
98                      CopyUtil.copy(is, os);
99                  } catch (final Exception e) {
100                     if (tempFile.exists() && !tempFile.delete()) {
101                         logger.warn("Failed to delete {}.", tempFile.getAbsolutePath());
102                     }
103                     logger.debug("Failed to copy {}", filename, e);
104                     throwValidationError(messages -> messages.addErrorsFailedToInstallPlugin(GLOBAL, filename), this::asListHtml);
105                 }
106                 new Thread(() -> {
107                     try {
108                         final PluginHelper pluginHelper = ComponentUtil.getPluginHelper();
109                         final Artifact artifact =
110                                 pluginHelper.getArtifactFromFileName(ArtifactType.UNKNOWN, filename, tempFile.getAbsolutePath());
111                         pluginHelper.installArtifact(artifact);
112                     } catch (final Exception e) {
113                         logger.warn("Failed to install {}", filename, e);
114                     } finally {
115                         if (tempFile.exists() && !tempFile.delete()) {
116                             logger.warn("Failed to delete {}.", tempFile.getAbsolutePath());
117                         }
118                     }
119                 }).start();
120                 saveInfo(messages -> messages.addSuccessInstallPlugin(GLOBAL, form.jarFile.getFileName()));
121             } else {
122                 final Artifact artifact = getArtifactFromInstallForm(form);
123                 if (artifact == null) {
124                     throwValidationError(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, form.id), this::asListHtml);
125                 }
126                 installArtifact(artifact);
127                 saveInfo(messages -> messages.addSuccessInstallPlugin(GLOBAL, artifact.getFileName()));
128             }
129         } catch (final ValidationErrorException e) {
130             throw e;
131         } catch (final Exception e) {
132             throwValidationError(messages -> messages.addErrorsFailedToInstallPlugin(GLOBAL, form.id), this::asListHtml);
133         }
134         return redirect(getClass());
135     }
136 
137     @Execute
138     @Secured({ ROLE })
139     public HtmlResponse installplugin() {
140         saveToken();
141         return asHtml(path_AdminPlugin_AdminPluginInstallpluginJsp).renderWith(data -> {
142             final List<Map<String, String>> result = new ArrayList<>();
143             final Map<String, String> map = new HashMap<>();
144             map.put("id", UPLOAD);
145             map.put("name", "");
146             map.put("version", "");
147             result.add(map);
148             try {
149                 result.addAll(getAllAvailableArtifacts());
150             } catch (final Exception e) {
151                 saveError(messages -> messages.addErrorsFailedToFindPlugins(GLOBAL));
152                 logger.warn("Failed to access a plugin repository.", e);
153             }
154             RenderDataUtil.register(data, "availableArtifactItems", result);
155         }).useForm(InstallForm.class, op -> op.setup(form -> {}));
156     }
157 
158     private HtmlResponse asListHtml() {
159         return asHtml(path_AdminPlugin_AdminPluginJsp)
160                 .renderWith(data -> data.register("installedArtifactItems", getAllInstalledArtifacts())).useForm(DeleteForm.class);
161     }
162 
163     public static List<Map<String, String>> getAllAvailableArtifacts() {
164         final PluginHelper pluginHelper = ComponentUtil.getPluginHelper();
165         final List<Map<String, String>> result = new ArrayList<>();
166         for (final PluginHelper.ArtifactType artifactType : PluginHelper.ArtifactType.values()) {
167             result.addAll(Arrays.stream(pluginHelper.getAvailableArtifacts(artifactType)).map(AdminPluginAction::beanToMap)
168                     .collect(Collectors.toList()));
169         }
170         return result;
171     }
172 
173     public static List<Map<String, String>> getAllInstalledArtifacts() {
174         final PluginHelper pluginHelper = ComponentUtil.getPluginHelper();
175         final List<Map<String, String>> result = new ArrayList<>();
176         for (final PluginHelper.ArtifactType artifactType : PluginHelper.ArtifactType.values()) {
177             result.addAll(Arrays.stream(pluginHelper.getInstalledArtifacts(artifactType)).map(AdminPluginAction::beanToMap)
178                     .collect(Collectors.toList()));
179         }
180         return result;
181     }
182 
183     public static Map<String, String> beanToMap(final Artifact artifact) {
184         final Map<String, String> item = new HashMap<>();
185         item.put("type", artifact.getType().getId());
186         item.put("id", artifact.getName() + ":" + artifact.getVersion());
187         item.put("name", artifact.getName());
188         item.put("version", artifact.getVersion());
189         item.put("url", artifact.getUrl());
190         return item;
191     }
192 
193     private Artifact getArtifactFromInstallForm(final InstallForm form) {
194         final String[] values = form.id.split(":");
195         return ComponentUtil.getPluginHelper().getArtifact(values[0], values[1]);
196     }
197 
198     public static void installArtifact(final Artifact artifact) {
199         new Thread(() -> {
200             final PluginHelper pluginHelper = ComponentUtil.getPluginHelper();
201             final Artifact[] artifacts = pluginHelper.getInstalledArtifacts(artifact.getType());
202             try {
203                 pluginHelper.installArtifact(artifact);
204             } catch (final Exception e) {
205                 logger.warn("Failed to install {}", artifact.getFileName(), e);
206             }
207             for (final Artifact a : artifacts) {
208                 if (a.getName().equals(artifact.getName()) && !a.getVersion().equals(artifact.getVersion())) {
209                     try {
210                         pluginHelper.deleteInstalledArtifact(a);
211                     } catch (final Exception e) {
212                         logger.warn("Failed to delete {}", a.getFileName(), e);
213                     }
214                 }
215             }
216         }).start();
217     }
218 
219     public static void deleteArtifact(final Artifact artifact) {
220         new Thread(() -> {
221             try {
222                 ComponentUtil.getPluginHelper().deleteInstalledArtifact(artifact);
223             } catch (final Exception e) {
224                 logger.warn("Failed to delete {}", artifact.getFileName(), e);
225             }
226         }).start();
227     }
228 }