/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the License at the
* following location:
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apereo.portal;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.lang.StringUtils;
import org.apereo.portal.utils.RandomTokenGenerator;
import org.apereo.portal.utils.threading.ReadResult;
import org.apereo.portal.utils.threading.ReadWriteCallback;
import org.apereo.portal.utils.threading.ReadWriteLockTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
*/
@Service("portalInfoProvider")
public class PortalInfoProviderImpl implements IPortalInfoProvider, ReadWriteCallback<String> {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private String serverName;
private String networkInterfaceName;
private String resolvedServerName;
private String resolvedUniqueServerName;
/** @param serverName A specific server name for {@link #getServerName()} to return */
@Value("${org.apereo.portal.PortalInfoProvider.serverName:}")
public void setServerName(String serverName) {
this.serverName = StringUtils.trimToNull(serverName);
}
/**
* @param networkInterfaceName The name to use to lookup a NetworkInterface via {@link
* NetworkInterface#getByName(String)}
*/
@Value("${org.apereo.portal.PortalInfoProvider.networkInterfaceName:}")
public void setNetworkInterfaceName(String networkInterfaceName) {
this.networkInterfaceName = StringUtils.trimToNull(networkInterfaceName);
}
private final ReadWriteLock serverNameResolutionLock = new ReentrantReadWriteLock();
@Override
public final String getServerName() {
return ReadWriteLockTemplate.doWithLock(serverNameResolutionLock, this);
}
@Override
public String getUniqueServerName() {
ReadWriteLockTemplate.doWithLock(serverNameResolutionLock, this);
return this.resolvedUniqueServerName;
}
@Override
public ReadResult<String> doInReadLock() {
if (this.resolvedServerName != null) {
return ReadResult.create(false, this.resolvedServerName);
}
return ReadResult.create(true);
}
@Override
public String doInWriteLock(ReadResult<String> readResult) {
this.resolvedServerName = resolveServerName();
this.resolvedUniqueServerName =
this.resolvedServerName
+ "_"
+ RandomTokenGenerator.INSTANCE.generateRandomToken(4);
return this.resolvedServerName;
}
protected String resolveServerName() {
if (this.serverName != null) {
return this.serverName;
}
String name = getNetworkInterfaceName(this.networkInterfaceName);
if (name != null) {
return name;
}
name = getLocalHostName();
if (name != null) {
return name;
}
name = getDefaultNetworkInterfaceName();
if (name != null) {
return name;
}
this.logger.warn(
"Failed to get serverName for NetworkInterface ("
+ this.networkInterfaceName
+ "), for InetAddress.getLocalHost(), for any NetworkInterface. Reverting to JVM instance specific UUID string.");
return UUID.randomUUID().toString();
}
protected String getDefaultNetworkInterfaceName() {
this.logger.info(
"Attempting to resolve serverName by iterating over NetworkInterface.getNetworkInterfaces()");
//Fail back to our best attempt at resolution
final Enumeration<NetworkInterface> networkInterfaceEnum;
try {
networkInterfaceEnum = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
logger.warn("Failed to get list of available NetworkInterfaces.", e);
return null;
}
//Use a local variable here to try and return the first hostName found that doesn't start with localhost
String name = null;
while (networkInterfaceEnum.hasMoreElements()) {
final NetworkInterface networkInterface = networkInterfaceEnum.nextElement();
for (Enumeration<InetAddress> inetAddressEnum = networkInterface.getInetAddresses();
inetAddressEnum.hasMoreElements();
) {
final InetAddress inetAddress = inetAddressEnum.nextElement();
name = inetAddress.getHostName();
if (!name.startsWith("localhost")) {
return name;
}
}
}
return name;
}
protected String getLocalHostName() {
this.logger.info("Attempting to resolve serverName using InetAddress.getLocalHost()");
final InetAddress localhost;
try {
localhost = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
logger.warn("Failed to find InetAddress for InetAddress.getLocalHost()", e);
return null;
}
return localhost.getHostName();
}
protected String getNetworkInterfaceName(String networkInterfaceName) {
if (networkInterfaceName == null) {
return null;
}
this.logger.info(
"Attempting to resolve serverName using NetworkInterface named ({})",
networkInterfaceName);
final NetworkInterface networkInterface;
try {
networkInterface = NetworkInterface.getByName(networkInterfaceName);
} catch (SocketException e) {
logger.warn(
"Failed to get NetworkInterface for name (" + networkInterfaceName + ").", e);
return null;
}
if (networkInterface == null) {
logger.warn(
"No NetworkInterface could be found for name ("
+ networkInterfaceName
+ "). Available interface names: "
+ getNetworkInterfaceNames());
return null;
}
final Enumeration<InetAddress> inetAddressesEnum = networkInterface.getInetAddresses();
if (!inetAddressesEnum.hasMoreElements()) {
logger.warn(
"NetworkInterface ("
+ networkInterface.getName()
+ ") has no InetAddresses to get a name from.");
return null;
}
final InetAddress inetAddress = inetAddressesEnum.nextElement();
if (inetAddressesEnum.hasMoreElements()) {
logger.warn(
"NetworkInterface ("
+ networkInterface.getName()
+ ") has more than one InetAddress, the hostName of the first will be returned.");
}
return inetAddress.getHostName();
}
protected Set<String> getNetworkInterfaceNames() {
final Enumeration<NetworkInterface> networkInterfacesEnum;
try {
networkInterfacesEnum = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
logger.warn("Failed to get list of available NetworkInterfaces.", e);
return Collections.emptySet();
}
final Set<String> names = new LinkedHashSet<String>();
while (networkInterfacesEnum.hasMoreElements()) {
final NetworkInterface networkInterface = networkInterfacesEnum.nextElement();
names.add(networkInterface.getName());
}
return names;
}
}