001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.plugininstaller.action;
016    
017    import com.liferay.portal.deploy.DeployUtil;
018    import com.liferay.portal.events.GlobalStartupAction;
019    import com.liferay.portal.kernel.deploy.auto.AutoDeployDir;
020    import com.liferay.portal.kernel.deploy.auto.AutoDeployListener;
021    import com.liferay.portal.kernel.deploy.auto.AutoDeployUtil;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.servlet.SessionErrors;
025    import com.liferay.portal.kernel.servlet.SessionMessages;
026    import com.liferay.portal.kernel.upload.UploadException;
027    import com.liferay.portal.kernel.upload.UploadPortletRequest;
028    import com.liferay.portal.kernel.util.ArrayUtil;
029    import com.liferay.portal.kernel.util.CharPool;
030    import com.liferay.portal.kernel.util.Constants;
031    import com.liferay.portal.kernel.util.FileUtil;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.HttpUtil;
034    import com.liferay.portal.kernel.util.ParamUtil;
035    import com.liferay.portal.kernel.util.PropsKeys;
036    import com.liferay.portal.kernel.util.ServerDetector;
037    import com.liferay.portal.kernel.util.StringBundler;
038    import com.liferay.portal.kernel.util.StringPool;
039    import com.liferay.portal.kernel.util.StringUtil;
040    import com.liferay.portal.kernel.util.Validator;
041    import com.liferay.portal.plugin.PluginPackageUtil;
042    import com.liferay.portal.plugin.RepositoryReport;
043    import com.liferay.portal.security.auth.PrincipalException;
044    import com.liferay.portal.security.permission.PermissionChecker;
045    import com.liferay.portal.struts.PortletAction;
046    import com.liferay.portal.theme.ThemeDisplay;
047    import com.liferay.portal.tools.deploy.BaseDeployer;
048    import com.liferay.portal.upload.ProgressInputStream;
049    import com.liferay.portal.util.HttpImpl;
050    import com.liferay.portal.util.PortalUtil;
051    import com.liferay.portal.util.PrefsPropsUtil;
052    import com.liferay.portal.util.PropsUtil;
053    import com.liferay.portal.util.PropsValues;
054    import com.liferay.portal.util.WebKeys;
055    
056    import java.io.File;
057    import java.io.FileOutputStream;
058    import java.io.IOException;
059    
060    import java.net.MalformedURLException;
061    import java.net.URL;
062    
063    import java.util.List;
064    
065    import javax.portlet.ActionRequest;
066    import javax.portlet.ActionResponse;
067    import javax.portlet.PortletConfig;
068    import javax.portlet.PortletPreferences;
069    
070    import javax.servlet.http.HttpServletResponse;
071    
072    import org.apache.commons.httpclient.HostConfiguration;
073    import org.apache.commons.httpclient.HttpClient;
074    import org.apache.commons.httpclient.methods.GetMethod;
075    import org.apache.struts.action.ActionForm;
076    import org.apache.struts.action.ActionMapping;
077    
078    /**
079     * @author Jorge Ferrer
080     * @author Brian Wing Shun Chan
081     * @author Minhchau Dang
082     */
083    public class InstallPluginAction extends PortletAction {
084    
085            @Override
086            public void processAction(
087                            ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
088                            ActionRequest actionRequest, ActionResponse actionResponse)
089                    throws Exception {
090    
091                    ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
092                            WebKeys.THEME_DISPLAY);
093    
094                    PermissionChecker permissionChecker =
095                            themeDisplay.getPermissionChecker();
096    
097                    if (!permissionChecker.isOmniadmin()) {
098                            SessionErrors.add(
099                                    actionRequest, PrincipalException.class.getName());
100    
101                            setForward(actionRequest, "portlet.plugin_installer.error");
102    
103                            return;
104                    }
105    
106                    String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
107    
108                    if (cmd.equals("deployConfiguration")) {
109                            deployConfiguration(actionRequest);
110                    }
111                    else if (cmd.equals("ignorePackages")) {
112                            ignorePackages(actionRequest);
113                    }
114                    else if (cmd.equals("localDeploy")) {
115                            localDeploy(actionRequest);
116                    }
117                    else if (cmd.equals("reloadRepositories")) {
118                            reloadRepositories(actionRequest);
119                    }
120                    else if (cmd.equals("remoteDeploy")) {
121                            remoteDeploy(actionRequest);
122                    }
123                    else if (cmd.equals("unignorePackages")) {
124                            unignorePackages(actionRequest);
125                    }
126                    else if (cmd.equals("uninstall")) {
127                            uninstall(actionRequest);
128                    }
129    
130                    sendRedirect(actionRequest, actionResponse);
131            }
132    
133            protected void deployConfiguration(ActionRequest actionRequest)
134                    throws Exception {
135    
136                    boolean enabled = ParamUtil.getBoolean(actionRequest, "enabled");
137                    String deployDir = ParamUtil.getString(actionRequest, "deployDir");
138                    String destDir = ParamUtil.getString(actionRequest, "destDir");
139                    long interval = ParamUtil.getLong(actionRequest, "interval");
140                    int blacklistThreshold = ParamUtil.getInteger(
141                            actionRequest, "blacklistThreshold");
142                    boolean unpackWar = ParamUtil.getBoolean(actionRequest, "unpackWar");
143                    boolean customPortletXml = ParamUtil.getBoolean(
144                            actionRequest, "customPortletXml");
145                    String jbossPrefix = ParamUtil.getString(actionRequest, "jbossPrefix");
146                    String tomcatConfDir = ParamUtil.getString(
147                            actionRequest, "tomcatConfDir");
148                    String tomcatLibDir = ParamUtil.getString(
149                            actionRequest, "tomcatLibDir");
150                    String pluginRepositoriesTrusted = ParamUtil.getString(
151                            actionRequest, "pluginRepositoriesTrusted");
152                    String pluginRepositoriesUntrusted = ParamUtil.getString(
153                            actionRequest, "pluginRepositoriesUntrusted");
154                    boolean pluginNotificationsEnabled = ParamUtil.getBoolean(
155                            actionRequest, "pluginNotificationsEnabled");
156                    String pluginPackagesIgnored = ParamUtil.getString(
157                            actionRequest, "pluginPackagesIgnored");
158    
159                    PortletPreferences preferences = PrefsPropsUtil.getPreferences();
160    
161                    preferences.setValue(
162                            PropsKeys.AUTO_DEPLOY_ENABLED, String.valueOf(enabled));
163                    preferences.setValue(PropsKeys.AUTO_DEPLOY_DEPLOY_DIR, deployDir);
164                    preferences.setValue(PropsKeys.AUTO_DEPLOY_DEST_DIR, destDir);
165                    preferences.setValue(
166                            PropsKeys.AUTO_DEPLOY_INTERVAL, String.valueOf(interval));
167                    preferences.setValue(
168                            PropsKeys.AUTO_DEPLOY_BLACKLIST_THRESHOLD,
169                            String.valueOf(blacklistThreshold));
170                    preferences.setValue(
171                            PropsKeys.AUTO_DEPLOY_UNPACK_WAR, String.valueOf(unpackWar));
172                    preferences.setValue(
173                            PropsKeys.AUTO_DEPLOY_CUSTOM_PORTLET_XML,
174                            String.valueOf(customPortletXml));
175                    preferences.setValue(PropsKeys.AUTO_DEPLOY_JBOSS_PREFIX, jbossPrefix);
176                    preferences.setValue(
177                            PropsKeys.AUTO_DEPLOY_TOMCAT_CONF_DIR, tomcatConfDir);
178                    preferences.setValue(
179                            PropsKeys.AUTO_DEPLOY_TOMCAT_LIB_DIR, tomcatLibDir);
180                    preferences.setValue(
181                            PropsKeys.PLUGIN_REPOSITORIES_TRUSTED, pluginRepositoriesTrusted);
182                    preferences.setValue(
183                            PropsKeys.PLUGIN_REPOSITORIES_UNTRUSTED,
184                            pluginRepositoriesUntrusted);
185                    preferences.setValue(
186                            PropsKeys.PLUGIN_NOTIFICATIONS_ENABLED,
187                            String.valueOf(pluginNotificationsEnabled));
188                    preferences.setValue(
189                            PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
190                            pluginPackagesIgnored);
191    
192                    preferences.store();
193    
194                    reloadRepositories(actionRequest);
195    
196                    if (_log.isInfoEnabled()) {
197                            _log.info("Unregistering auto deploy directories");
198                    }
199    
200                    AutoDeployUtil.unregisterDir("defaultAutoDeployDir");
201    
202                    if (enabled) {
203                            if (_log.isInfoEnabled()) {
204                                    _log.info("Registering auto deploy directories");
205                            }
206    
207                            List<AutoDeployListener> autoDeployListeners =
208                                    GlobalStartupAction.getAutoDeployListeners();
209    
210                            AutoDeployDir autoDeployDir = new AutoDeployDir(
211                                    "defaultAutoDeployDir", new File(deployDir), new File(destDir),
212                                    interval, blacklistThreshold, autoDeployListeners);
213    
214                            AutoDeployUtil.registerDir(autoDeployDir);
215                    }
216                    else {
217                            if (_log.isInfoEnabled()) {
218                                    _log.info("Not registering auto deploy directories");
219                            }
220                    }
221            }
222    
223            protected String[] getSourceForgeMirrors() {
224                    return PropsUtil.getArray(PropsKeys.SOURCE_FORGE_MIRRORS);
225            }
226    
227            protected void ignorePackages(ActionRequest actionRequest)
228                    throws Exception {
229    
230                    String pluginPackagesIgnored = ParamUtil.getString(
231                            actionRequest, "pluginPackagesIgnored");
232    
233                    String oldPluginPackagesIgnored = PrefsPropsUtil.getString(
234                            PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
235    
236                    PortletPreferences preferences = PrefsPropsUtil.getPreferences();
237    
238                    if (Validator.isNotNull(oldPluginPackagesIgnored)) {
239                            preferences.setValue(
240                                    PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
241                                    oldPluginPackagesIgnored.concat(StringPool.NEW_LINE).concat(
242                                            pluginPackagesIgnored));
243                    }
244                    else {
245                            preferences.setValue(
246                                    PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
247                                    pluginPackagesIgnored);
248                    }
249    
250                    preferences.store();
251    
252                    PluginPackageUtil.refreshUpdatesAvailableCache();
253            }
254    
255            protected void localDeploy(ActionRequest actionRequest) throws Exception {
256                    UploadPortletRequest uploadPortletRequest =
257                            PortalUtil.getUploadPortletRequest(actionRequest);
258    
259                    String fileName = null;
260    
261                    String deploymentContext = ParamUtil.getString(
262                            actionRequest, "deploymentContext");
263    
264                    if (Validator.isNotNull(deploymentContext)) {
265                            fileName =
266                                    BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
267                    }
268                    else {
269                            fileName = GetterUtil.getString(uploadPortletRequest.getFileName(
270                                    "file"));
271    
272                            int pos = fileName.lastIndexOf(CharPool.PERIOD);
273    
274                            if (pos != -1) {
275                                    deploymentContext = fileName.substring(0, pos);
276                            }
277                    }
278    
279                    File file = uploadPortletRequest.getFile("file");
280    
281                    byte[] bytes = FileUtil.getBytes(file);
282    
283                    if ((bytes == null) || (bytes.length == 0)) {
284                            SessionErrors.add(actionRequest, UploadException.class.getName());
285    
286                            return;
287                    }
288    
289                    try {
290                            PluginPackageUtil.registerPluginPackageInstallation(
291                                    deploymentContext);
292    
293                            String source = file.toString();
294    
295                            String deployDir = PrefsPropsUtil.getString(
296                                    PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
297                                    PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
298    
299                            String destination = deployDir + StringPool.SLASH + fileName;
300    
301                            FileUtil.copyFile(source, destination);
302    
303                            SessionMessages.add(actionRequest, "pluginUploaded");
304                    }
305                    finally {
306                            PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
307                    }
308            }
309    
310            protected void reloadRepositories(ActionRequest actionRequest)
311                    throws Exception {
312    
313                    RepositoryReport repositoryReport =
314                            PluginPackageUtil.reloadRepositories();
315    
316                    SessionMessages.add(
317                            actionRequest, WebKeys.PLUGIN_REPOSITORY_REPORT, repositoryReport);
318            }
319    
320            protected void remoteDeploy(ActionRequest actionRequest) throws Exception {
321                    try {
322                            String url = ParamUtil.getString(actionRequest, "url");
323    
324                            URL urlObj = new URL(url);
325    
326                            String host = urlObj.getHost();
327    
328                            if (host.endsWith(".sf.net") || host.endsWith(".sourceforge.net")) {
329                                    remoteDeploySourceForge(urlObj.getPath(), actionRequest);
330                            }
331                            else {
332                                    remoteDeploy(url, urlObj, actionRequest, true);
333                            }
334                    }
335                    catch (MalformedURLException murle) {
336                            SessionErrors.add(actionRequest, "invalidUrl", murle);
337                    }
338            }
339    
340            protected int remoteDeploy(
341                            String url, URL urlObj, ActionRequest actionRequest,
342                            boolean failOnError)
343                    throws Exception {
344    
345                    int responseCode = HttpServletResponse.SC_OK;
346    
347                    GetMethod getMethod = null;
348    
349                    String deploymentContext = ParamUtil.getString(
350                            actionRequest, "deploymentContext");
351    
352                    try {
353                            HttpImpl httpImpl = (HttpImpl)HttpUtil.getHttp();
354    
355                            HostConfiguration hostConfiguration = httpImpl.getHostConfiguration(
356                                    url);
357    
358                            HttpClient httpClient = httpImpl.getClient(hostConfiguration);
359    
360                            getMethod = new GetMethod(url);
361    
362                            String fileName = null;
363    
364                            if (Validator.isNotNull(deploymentContext)) {
365                                    fileName =
366                                            BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
367                            }
368                            else {
369                                    fileName = url.substring(url.lastIndexOf(CharPool.SLASH) + 1);
370    
371                                    int pos = fileName.lastIndexOf(CharPool.PERIOD);
372    
373                                    if (pos != -1) {
374                                            deploymentContext = fileName.substring(0, pos);
375                                    }
376                            }
377    
378                            PluginPackageUtil.registerPluginPackageInstallation(
379                                    deploymentContext);
380    
381                            responseCode = httpClient.executeMethod(
382                                    hostConfiguration, getMethod);
383    
384                            if (responseCode != HttpServletResponse.SC_OK) {
385                                    if (failOnError) {
386                                            SessionErrors.add(
387                                                    actionRequest, "errorConnectingToUrl",
388                                                    new Object[] {String.valueOf(responseCode)});
389                                    }
390    
391                                    return responseCode;
392                            }
393    
394                            long contentLength = getMethod.getResponseContentLength();
395    
396                            String progressId = ParamUtil.getString(
397                                    actionRequest, Constants.PROGRESS_ID);
398    
399                            ProgressInputStream pis = new ProgressInputStream(
400                                    actionRequest, getMethod.getResponseBodyAsStream(),
401                                    contentLength, progressId);
402    
403                            String deployDir = PrefsPropsUtil.getString(
404                                    PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
405                                    PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
406    
407                            String tmpFilePath =
408                                    deployDir + StringPool.SLASH + _DOWNLOAD_DIR +
409                                            StringPool.SLASH + fileName;
410    
411                            File tmpFile = new File(tmpFilePath);
412    
413                            if (!tmpFile.getParentFile().exists()) {
414                                    tmpFile.getParentFile().mkdirs();
415                            }
416    
417                            FileOutputStream fos = new FileOutputStream(tmpFile);
418    
419                            try {
420                                    pis.readAll(fos);
421    
422                                    if (_log.isInfoEnabled()) {
423                                            _log.info(
424                                                    "Downloaded plugin from " + urlObj + " has " +
425                                                            pis.getTotalRead() + " bytes");
426                                    }
427                            }
428                            finally {
429                                    pis.clearProgress();
430                            }
431    
432                            getMethod.releaseConnection();
433    
434                            if (pis.getTotalRead() > 0) {
435                                    String destination = deployDir + StringPool.SLASH + fileName;
436    
437                                    File destinationFile = new File(destination);
438    
439                                    boolean moved = FileUtil.move(tmpFile, destinationFile);
440    
441                                    if (!moved) {
442                                            FileUtil.copyFile(tmpFile, destinationFile);
443                                            FileUtil.delete(tmpFile);
444                                    }
445    
446                                    SessionMessages.add(actionRequest, "pluginDownloaded");
447                            }
448                            else {
449                                    if (failOnError) {
450                                            SessionErrors.add(
451                                                    actionRequest, UploadException.class.getName());
452                                    }
453    
454                                    responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
455                            }
456                    }
457                    catch (MalformedURLException murle) {
458                            SessionErrors.add(actionRequest, "invalidUrl", murle);
459                    }
460                    catch (IOException ioe) {
461                            SessionErrors.add(actionRequest, "errorConnectingToUrl", ioe);
462                    }
463                    finally {
464                            if (getMethod != null) {
465                                    getMethod.releaseConnection();
466                            }
467    
468                            PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
469                    }
470    
471                    return responseCode;
472            }
473    
474            protected void remoteDeploySourceForge(
475                            String path, ActionRequest actionRequest)
476                    throws Exception {
477    
478                    String[] sourceForgeMirrors = getSourceForgeMirrors();
479    
480                    for (int i = 0; i < sourceForgeMirrors.length; i++) {
481                            try {
482                                    String url = sourceForgeMirrors[i] + path;
483    
484                                    if (_log.isDebugEnabled()) {
485                                            _log.debug("Downloading from SourceForge mirror " + url);
486                                    }
487    
488                                    URL urlObj = new URL(url);
489    
490                                    boolean failOnError = false;
491    
492                                    if ((i + 1) == sourceForgeMirrors.length) {
493                                            failOnError = true;
494                                    }
495    
496                                    int responseCode = remoteDeploy(
497                                            url, urlObj, actionRequest, failOnError);
498    
499                                    if (responseCode == HttpServletResponse.SC_OK) {
500                                            return;
501                                    }
502                            }
503                            catch (MalformedURLException murle) {
504                                    SessionErrors.add(actionRequest, "invalidUrl", murle);
505                            }
506                    }
507            }
508    
509            protected void unignorePackages(ActionRequest actionRequest)
510                    throws Exception {
511    
512                    String[] pluginPackagesUnignored = StringUtil.splitLines(
513                            ParamUtil.getString(actionRequest, "pluginPackagesUnignored"));
514    
515                    String[] pluginPackagesIgnored = PrefsPropsUtil.getStringArray(
516                            PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
517                            StringPool.NEW_LINE,
518                            PropsValues.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
519    
520                    StringBundler sb = new StringBundler();
521    
522                    for (int i = 0; i < pluginPackagesIgnored.length; i++) {
523                            String packageId = pluginPackagesIgnored[i];
524    
525                            if (!ArrayUtil.contains(pluginPackagesUnignored, packageId)) {
526                                    sb.append(packageId);
527                                    sb.append(StringPool.NEW_LINE);
528                            }
529                    }
530    
531                    PortletPreferences preferences = PrefsPropsUtil.getPreferences();
532    
533                    preferences.setValue(
534                            PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED, sb.toString());
535    
536                    preferences.store();
537    
538                    PluginPackageUtil.refreshUpdatesAvailableCache();
539            }
540    
541            protected void uninstall(ActionRequest actionRequest) throws Exception {
542                    String appServerType = ServerDetector.getServerId();
543    
544                    String deploymentContext = ParamUtil.getString(
545                            actionRequest, "deploymentContext");
546    
547                    if (appServerType.startsWith(ServerDetector.JBOSS_ID)) {
548                            deploymentContext += ".war";
549                    }
550    
551                    File deployDir = new File(
552                            DeployUtil.getAutoDeployDestDir() + "/" + deploymentContext);
553    
554                    DeployUtil.undeploy(appServerType, deployDir);
555    
556                    SessionMessages.add(actionRequest, "triggeredPortletUndeploy");
557            }
558    
559            private static final String _DOWNLOAD_DIR = "download";
560    
561            private static Log _log = LogFactoryUtil.getLog(InstallPluginAction.class);
562    
563    }