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}