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}