001/** 002 * 003 * Copyright 2019-2020 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.c2s; 018 019import java.io.PrintWriter; 020import java.io.StringWriter; 021import java.lang.reflect.Constructor; 022import java.lang.reflect.InvocationTargetException; 023import java.util.Arrays; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.Map; 028import java.util.Set; 029 030import org.jivesoftware.smack.ConnectionConfiguration; 031import org.jivesoftware.smack.SmackConfiguration; 032import org.jivesoftware.smack.fsm.StateDescriptor; 033import org.jivesoftware.smack.fsm.StateDescriptorGraph; 034import org.jivesoftware.smack.fsm.StateDescriptorGraph.GraphVertex; 035import org.jivesoftware.smack.util.CollectionUtil; 036 037public final class ModularXmppClientToServerConnectionConfiguration extends ConnectionConfiguration { 038 039 final Set<ModularXmppClientToServerConnectionModuleDescriptor> moduleDescriptors; 040 041 final GraphVertex<StateDescriptor> initialStateDescriptorVertex; 042 043 private ModularXmppClientToServerConnectionConfiguration(Builder builder) { 044 super(builder); 045 046 moduleDescriptors = Collections.unmodifiableSet(CollectionUtil.newSetWith(builder.modulesDescriptors.values())); 047 048 Set<Class<? extends StateDescriptor>> backwardEdgeStateDescriptors = new HashSet<>(); 049 // Add backward edges from configured connection modules. Note that all state descriptors from module 050 // descriptors are backwards edges. 051 for (ModularXmppClientToServerConnectionModuleDescriptor moduleDescriptor : moduleDescriptors) { 052 Set<Class<? extends StateDescriptor>> moduleStateDescriptors = moduleDescriptor.getStateDescriptors(); 053 backwardEdgeStateDescriptors.addAll(moduleStateDescriptors); 054 } 055 056 try { 057 initialStateDescriptorVertex = StateDescriptorGraph.constructStateDescriptorGraph(backwardEdgeStateDescriptors); 058 } 059 catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException 060 | NoSuchMethodException | SecurityException e) { 061 // TODO: Depending on the exact exception thrown, this potentially indicates an invalid connection 062 // configuration, e.g. there is no edge from disconnected to connected. 063 throw new IllegalStateException(e); 064 } 065 } 066 067 public void printStateGraphInDotFormat(PrintWriter pw, boolean breakStateName) { 068 StateDescriptorGraph.stateDescriptorGraphToDot(Collections.singleton(initialStateDescriptorVertex), pw, 069 breakStateName); 070 } 071 072 public String getStateGraphInDotFormat() { 073 StringWriter sw = new StringWriter(); 074 PrintWriter pw = new PrintWriter(sw); 075 076 printStateGraphInDotFormat(pw, true); 077 078 return sw.toString(); 079 } 080 081 public static Builder builder() { 082 return new Builder(); 083 } 084 085 public static final class Builder 086 extends ConnectionConfiguration.Builder<Builder, ModularXmppClientToServerConnectionConfiguration> { 087 088 private final Map<Class<? extends ModularXmppClientToServerConnectionModuleDescriptor>, ModularXmppClientToServerConnectionModuleDescriptor> modulesDescriptors = new HashMap<>(); 089 090 private Builder() { 091 SmackConfiguration.addAllKnownModulesTo(this); 092 } 093 094 @Override 095 public ModularXmppClientToServerConnectionConfiguration build() { 096 return new ModularXmppClientToServerConnectionConfiguration(this); 097 } 098 099 void addModule(ModularXmppClientToServerConnectionModuleDescriptor connectionModule) { 100 Class<? extends ModularXmppClientToServerConnectionModuleDescriptor> moduleDescriptorClass = connectionModule.getClass(); 101 if (modulesDescriptors.containsKey(moduleDescriptorClass)) { 102 throw new IllegalArgumentException("A connection module for " + moduleDescriptorClass + " is already configured"); 103 } 104 modulesDescriptors.put(moduleDescriptorClass, connectionModule); 105 } 106 107 @SuppressWarnings("unchecked") 108 public Builder addModule(Class<? extends ModularXmppClientToServerConnectionModuleDescriptor> moduleClass) { 109 Class<?>[] declaredClasses = moduleClass.getDeclaredClasses(); 110 111 Class<? extends ModularXmppClientToServerConnectionModuleDescriptor.Builder> builderClass = null; 112 for (Class<?> declaredClass : declaredClasses) { 113 if (!ModularXmppClientToServerConnectionModuleDescriptor.Builder.class.isAssignableFrom(declaredClass)) { 114 continue; 115 } 116 117 builderClass = (Class<? extends ModularXmppClientToServerConnectionModuleDescriptor.Builder>) declaredClass; 118 break; 119 } 120 121 if (builderClass == null) { 122 throw new IllegalArgumentException( 123 "Found no builder for " + moduleClass + ". Delcared classes: " + Arrays.toString(declaredClasses)); 124 } 125 126 return with(builderClass).buildModule(); 127 } 128 129 public <B extends ModularXmppClientToServerConnectionModuleDescriptor.Builder> B with( 130 Class<? extends B> moduleDescriptorBuilderClass) { 131 Constructor<? extends B> moduleDescriptorBuilderCosntructor; 132 try { 133 moduleDescriptorBuilderCosntructor = moduleDescriptorBuilderClass.getDeclaredConstructor( 134 ModularXmppClientToServerConnectionConfiguration.Builder.class); 135 } catch (NoSuchMethodException | SecurityException e) { 136 throw new IllegalArgumentException(e); 137 } 138 139 moduleDescriptorBuilderCosntructor.setAccessible(true); 140 141 B moduleDescriptorBuilder; 142 try { 143 moduleDescriptorBuilder = moduleDescriptorBuilderCosntructor.newInstance(this); 144 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException 145 | InvocationTargetException e) { 146 throw new IllegalArgumentException(e); 147 } 148 149 return moduleDescriptorBuilder; 150 } 151 152 public Builder removeModule(Class<? extends ModularXmppClientToServerConnectionModuleDescriptor> moduleClass) { 153 modulesDescriptors.remove(moduleClass); 154 return getThis(); 155 } 156 157 public Builder removeAllModules() { 158 modulesDescriptors.clear(); 159 return getThis(); 160 } 161 162 @Override 163 protected Builder getThis() { 164 return this; 165 } 166 } 167}