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