001/*
002 * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 */
018
019package org.nuxeo.connect.client.jsf;
020
021import java.io.IOException;
022import java.io.Serializable;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.Map;
026
027import javax.faces.context.FacesContext;
028import javax.faces.model.SelectItem;
029import javax.servlet.http.HttpServletRequest;
030
031import org.apache.commons.codec.binary.Base64;
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.jboss.seam.ScopeType;
035import org.jboss.seam.annotations.Factory;
036import org.jboss.seam.annotations.In;
037import org.jboss.seam.annotations.Name;
038import org.jboss.seam.annotations.Scope;
039import org.jboss.seam.contexts.Contexts;
040import org.jboss.seam.faces.FacesMessages;
041import org.jboss.seam.international.StatusMessage;
042import org.nuxeo.common.utils.ExceptionUtils;
043import org.nuxeo.connect.client.status.ConnectStatusHolder;
044import org.nuxeo.connect.client.status.ConnectUpdateStatusInfo;
045import org.nuxeo.connect.client.status.SubscriptionStatusWrapper;
046import org.nuxeo.connect.connector.NuxeoClientInstanceType;
047import org.nuxeo.connect.connector.http.ConnectUrlConfig;
048import org.nuxeo.connect.data.SubscriptionStatusType;
049import org.nuxeo.connect.identity.LogicalInstanceIdentifier;
050import org.nuxeo.connect.identity.LogicalInstanceIdentifier.InvalidCLID;
051import org.nuxeo.connect.identity.LogicalInstanceIdentifier.NoCLID;
052import org.nuxeo.connect.identity.TechnicalInstanceIdentifier;
053import org.nuxeo.connect.registration.ConnectRegistrationService;
054import org.nuxeo.connect.update.PackageException;
055import org.nuxeo.connect.update.PackageUpdateService;
056import org.nuxeo.ecm.core.api.Blob;
057import org.nuxeo.ecm.core.api.CloseableFile;
058import org.nuxeo.runtime.api.Framework;
059
060/**
061 * Seam Bean to expose Connect Registration operations.
062 * <ul>
063 * <li>getting status
064 * <li>registering
065 * <li>...
066 * </ul>
067 *
068 * @author <a href="mailto:td@nuxeo.com">Thierry Delprat</a>
069 */
070@Name("connectStatus")
071@Scope(ScopeType.CONVERSATION)
072public class ConnectStatusActionBean implements Serializable {
073
074    private static final long serialVersionUID = 1L;
075
076    public static final String CLIENT_BANNER_TYPE = "clientSideBanner";
077
078    public static final String SERVER_BANNER_TYPE = "serverSideBanner";
079
080    private static final Log log = LogFactory.getLog(ConnectStatusActionBean.class);
081
082    @In(create = true, required = false)
083    protected FacesMessages facesMessages;
084
085    @In(create = true, required = true, value = "appsViews")
086    protected AppCenterViewsManager appsViews;
087
088    @In(create = true)
089    protected Map<String, String> messages;
090
091    protected String CLID;
092
093    protected String token;
094
095    protected ConnectUpdateStatusInfo connectionStatusCache;
096
097    public String getRegistredCLID() throws NoCLID {
098        if (isRegistred()) {
099            return LogicalInstanceIdentifier.instance().getCLID();
100        } else {
101            return null;
102        }
103    }
104
105    public String getCLID() {
106        return CLID;
107    }
108
109    public void setCLID(String cLID) {
110        CLID = cLID;
111    }
112
113    public String unregister() {
114        LogicalInstanceIdentifier.cleanUp();
115        resetRegister();
116        return null;
117    }
118
119    public List<SelectItem> getInstanceTypes() {
120        List<SelectItem> types = new ArrayList<>();
121        for (NuxeoClientInstanceType itype : NuxeoClientInstanceType.values()) {
122            SelectItem item = new SelectItem(itype.getValue(), "label.instancetype." + itype.getValue());
123            types.add(item);
124        }
125        return types;
126    }
127
128    protected ConnectRegistrationService getService() {
129        return Framework.getLocalService(ConnectRegistrationService.class);
130    }
131
132    @Factory(scope = ScopeType.APPLICATION, value = "registredConnectInstance")
133    public boolean isRegistred() {
134        return getService().isInstanceRegistred();
135    }
136
137    protected void flushContextCache() {
138        // A4J and Event cache don't play well ...
139        Contexts.getApplicationContext().remove("registredConnectInstance");
140        Contexts.getApplicationContext().remove("connectUpdateStatusInfo");
141        appsViews.flushCache();
142    }
143
144    @Factory(value = "connectServerReachable", scope = ScopeType.EVENT)
145    public boolean isConnectServerReachable() {
146        return !ConnectStatusHolder.instance().getStatus().isConnectServerUnreachable();
147    }
148
149    public String refreshStatus() {
150        ConnectStatusHolder.instance().getStatus(true);
151        flushContextCache();
152        return null;
153    }
154
155    public SubscriptionStatusWrapper getStatus() {
156        return ConnectStatusHolder.instance().getStatus();
157    }
158
159    public String resetRegister() {
160        flushContextCache();
161        return null;
162    }
163
164    public String getToken() {
165        return token;
166    }
167
168    public void setToken(String token) throws IOException, InvalidCLID {
169        if (token != null) {
170            String tokenData = new String(Base64.decodeBase64(token));
171            String[] tokenDataLines = tokenData.split("\n");
172            for (String line : tokenDataLines) {
173                String[] parts = line.split(":");
174                if (parts.length > 1 && "CLID".equals(parts[0])) {
175                    getService().localRegisterInstance(parts[1], " ");
176                    // force refresh of connect status info
177                    connectionStatusCache = null;
178                    flushContextCache();
179                    ConnectStatusHolder.instance().flush();
180                }
181            }
182        }
183    }
184
185    public String getCTID() {
186        try {
187            return TechnicalInstanceIdentifier.instance().getCTID();
188        } catch (Exception e) { // stupid API
189            throw ExceptionUtils.runtimeException(e);
190        }
191    }
192
193    public String localRegister() {
194        try {
195            getService().localRegisterInstance(CLID, "");
196        } catch (InvalidCLID e) {
197            facesMessages.addToControl("offline_clid", StatusMessage.Severity.WARN,
198                    messages.get("label.connect.wrongCLID"));
199        } catch (IOException e) {
200            facesMessages.addToControl("offline_clid", StatusMessage.Severity.ERROR,
201                    messages.get("label.connect.registrationError"));
202            log.error("Error while registering instance locally", e);
203        }
204        flushContextCache();
205        return null;
206    }
207
208    protected Blob packageToUpload;
209
210    protected String packageFileName;
211
212    public String getPackageFileName() {
213        return packageFileName;
214    }
215
216    public void setPackageFileName(String packageFileName) {
217        this.packageFileName = packageFileName;
218    }
219
220    public Blob getPackageToUpload() {
221        return packageToUpload;
222    }
223
224    public void setPackageToUpload(Blob packageToUpload) {
225        this.packageToUpload = packageToUpload;
226    }
227
228    public void uploadPackage() throws IOException {
229        if (packageToUpload == null) {
230            facesMessages.add(StatusMessage.Severity.WARN, "label.connect.nofile");
231            return;
232        }
233        PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class);
234        try (CloseableFile cfile = packageToUpload.getCloseableFile()) {
235            pus.addPackage(cfile.getFile());
236        } catch (PackageException e) {
237            log.warn(e, e);
238            facesMessages.add(StatusMessage.Severity.ERROR,
239                    messages.get("label.connect.wrong.package") + ":" + e.getMessage());
240            return;
241        } finally {
242            packageFileName = null;
243            packageToUpload = null;
244        }
245    }
246
247    public ConnectUpdateStatusInfo getDynamicConnectUpdateStatusInfo() {
248        HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
249        String bannerType = req.getParameter("bannerType");
250        if ("unregistered".equals(bannerType)) {
251            return ConnectUpdateStatusInfo.unregistered();
252        } else if ("notreachable".equals(bannerType)) {
253            return ConnectUpdateStatusInfo.connectServerUnreachable();
254        } else if ("notvalid".equals(bannerType)) {
255            return ConnectUpdateStatusInfo.notValid();
256        } else if ("ok".equals(bannerType)) {
257            return ConnectUpdateStatusInfo.ok();
258        }
259        return getConnectUpdateStatusInfo();
260    }
261
262    /**
263     * @since 5.9.2
264     */
265    @Factory(scope = ScopeType.APPLICATION, value = "connectBannerEnabled")
266    public boolean isConnectBannerEnabled() {
267        final String testerName = Framework.getProperty("org.nuxeo.ecm.tester.name");
268        if (testerName != null && testerName.equals("Nuxeo-Selenium-Tester")) {
269            // disable banner when running selenium tests
270            return false;
271        }
272        return true;
273    }
274
275    @Factory(scope = ScopeType.APPLICATION, value = "connectUpdateStatusInfo")
276    public ConnectUpdateStatusInfo getConnectUpdateStatusInfo() {
277        if (connectionStatusCache == null) {
278            if (!isRegistred()) {
279                connectionStatusCache = ConnectUpdateStatusInfo.unregistered();
280            } else {
281                if (isConnectBannerEnabled() && isConnectServerReachable()) {
282                    if (getStatus().isError()) {
283                        connectionStatusCache = ConnectUpdateStatusInfo.connectServerUnreachable();
284                    } else {
285                        if (ConnectStatusHolder.instance().getStatus().status() == SubscriptionStatusType.OK) {
286                            connectionStatusCache = ConnectUpdateStatusInfo.ok();
287                        } else {
288                            connectionStatusCache = ConnectUpdateStatusInfo.notValid();
289                        }
290                    }
291                } else {
292                    connectionStatusCache = ConnectUpdateStatusInfo.connectServerUnreachable();
293                }
294            }
295        }
296        return connectionStatusCache;
297    }
298
299    @Factory("nuxeoConnectUrl")
300    public String getConnectServerUrl() {
301        return ConnectUrlConfig.getBaseUrl();
302    }
303
304}