/*
* Copyright 2001-2006 The Apache Software Foundation.
*
* Licensed 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
/*
* Copyright 2008-2009 MOPAS(Ministry of Public Administration and Security).
*
* Licensed 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 egovframework.rte.fdl.idgnr.impl;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.UUID;
import egovframework.rte.fdl.cmmn.exception.FdlException;
import egovframework.rte.fdl.idgnr.EgovIdGnrService;
import egovframework.rte.fdl.idgnr.EgovIdGnrStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.MessageSource;
/**
* ID Generation 서비스를 위한 UUID 구현 클래스
*
* <p><b>NOTE</b>: UUID(Universally Unique Identifier) 알고리즘 기반의 유일키를 제공 받을 수 있다.</p>
*
* @author 실행환경 개발팀 김태호
* @since 2009.02.01
* @version 1.0
* @see <pre>
* == 개정이력(Modification Information) ==
*
* 수정일 수정자 수정내용
* ------- -------- ---------------------------
* 2009.02.01 김태호 최초 생성
* 2014.08.18 한성곤 UUID 오류 수정
*
* </pre>
*/
public class EgovUUIdGnrServiceImpl implements EgovIdGnrService, ApplicationContextAware {
/**
* Message Source
*/
private MessageSource messageSource;
/**
* Message Source Injection
*/
public void setApplicationContext(ApplicationContext applicationContext) {
this.messageSource = (MessageSource) applicationContext.getBean("messageSource");
}
/**
* Class 사용 로거 지정
*/
private static final Logger LOGGER = LoggerFactory.getLogger(EgovUUIdGnrServiceImpl.class);
private static final String ERROR_STRING = "address in the configuration should be a valid IP or MAC Address";
/**
* Address Id
*/
private String mAddressId;
/**
* MAC Address
*/
private long hostId;
/**
* BigDecimal 타입을 아이디 제공
*
* @return BigDecimal 타입 ID
* @throws FdlException 아이디 생성에 실패한 경우
*/
public BigDecimal getNextBigDecimalId() throws FdlException {
String newId = getNextStringId().replaceAll("-", "");
// CHECKSTYLE:OFF
BigInteger bi = new BigInteger(newId, 16);
// CHECKSTYLE:ON
BigDecimal bd = new BigDecimal(bi);
return bd;
}
/**
* byte 타입을 아이디 제공
*
* @return byte 타입 ID
* @throws FdlException 아이디 생성에 실패한 경우
*/
public byte getNextByteId() throws FdlException {
throw new FdlException(messageSource, "error.idgnr.not.supported", new String[] { "Byte" }, null);
}
/**
* int 타입을 아이디 제공을 요청하면 불가능한 요청이라는 Exception 발생
*
* @return int 타입 ID
* @throws FdlException 아이디 생성에 실패한 경우
*/
public int getNextIntegerId() throws FdlException {
throw new FdlException(messageSource, "error.idgnr.not.supported", new String[] { "Integer" }, null);
}
/**
* long 타입을 아이디 제공을 요청하면 불가능한 요청이라는 Exception 발생
*
* @return long 타입 ID
* @throws FdlException 아이디 생성에 실패한 경우
*/
public long getNextLongId() throws FdlException {
throw new FdlException(messageSource, "error.idgnr.not.supported", new String[] { "Long" }, null);
}
/**
* short 타입을 아이디 제공을 요청하면 불가능한 요청이라는 Exception 발생
*
* @return short 타입 ID
* @throws FdlException 아이디 생성에 실패한 경우
*/
public short getNextShortId() throws FdlException {
throw new FdlException(messageSource, "error.idgnr.not.supported", new String[] { "Short" }, null);
}
/**
* String 타입을 아이디 제공
*
* @return String 타입 ID
* @throws FdlException 아이디 생성에 실패한 경우
*/
public String getNextStringId() throws FdlException {
return getUUId();
}
/**
* 정책정보를 입력받아 String 타입을 아이디 제공을 요청하면 불가능한 요청이라는 에러 발생
*
* @param strategy 정책정보 오브젝트
* @return String 타입 ID
* @throws FdlException 아이디 생성에 실패한 경우
*/
public String getNextStringId(EgovIdGnrStrategy strategy) throws FdlException {
throw new FdlException(messageSource, "error.idgnr.not.supported", new String[] { "String" }, null);
}
/**
* 정책정보를 입력받아 String 타입을 아이디 제공을 요청하면 불가능한 요청이라는 에러 발생
*
* @param strategy 정책 String
* @return String 타입 ID
* @throws FdlException 아이디 생성에 실패한 경우
*/
public String getNextStringId(String strategyId) throws FdlException {
throw new FdlException(messageSource, "error.idgnr.not.supported", new String[] { "String" }, null);
}
/**
* Config 정보에 지정된 Address 세팅
*
* @param address Config 에 지정된 address 정보
* @throws FdlException IP 정보가 이상한 경우
*/
public void setAddress(String address) throws FdlException {
// CHECKSTYLE:OFF
// this.address = address;
byte[] addressBytes = new byte[6];
if (null == address) {
LOGGER.warn("IDGeneration Service : Using a random number as the base for id's."
+ "This is not the best method for many purposes, but may be adequate in some circumstances."
+ " Consider using an IP or ethernet (MAC) address if available. ");
for (int i = 0; i < 6; i++) {
addressBytes[i] = (byte) (255 * Math.random());
}
} else {
if (address.indexOf(".") > 0) {
// we should have an IP
StringTokenizer stok = new StringTokenizer(address, ".");
if (stok.countTokens() != 4) {
throw new FdlException(ERROR_STRING);
}
addressBytes[0] = (byte) 255;
addressBytes[1] = (byte) 255;
int i = 2;
try {
while (stok.hasMoreTokens()) {
addressBytes[i++] = Integer.valueOf(stok.nextToken(), 16).byteValue();
}
} catch (Exception e) {
throw new FdlException(ERROR_STRING);
}
} else if (address.indexOf(":") > 0) {
// we should have a MAC
StringTokenizer stok = new StringTokenizer(address, ":");
if (stok.countTokens() != 6) {
throw new FdlException(ERROR_STRING);
}
int i = 0;
try {
while (stok.hasMoreTokens()) {
addressBytes[i++] = Integer.valueOf(stok.nextToken(), 16).byteValue();
}
} catch (Exception e) {
throw new FdlException(ERROR_STRING);
}
} else {
throw new FdlException(ERROR_STRING);
}
}
// CHECKSTYLE:ON
mAddressId = TimeBasedUUIDGenerator.getMacAddressAsString(addressBytes);
hostId = TimeBasedUUIDGenerator.getMacAddressAsLong(addressBytes);
LOGGER.debug("Address Id : " + mAddressId);
}
/**
* UUID 얻기
* @return String unique id
*/
private String getUUId() {
if (mAddressId == null) {
return UUID.randomUUID().toString();
} else {
return TimeBasedUUIDGenerator.generateId(hostId).toString();
}
}
}
/**
* Will generate time-based UUID (version 1 UUID).
* Requires JDK 1.6+
*
* @author Oleg Zhurakousky
*/
final class TimeBasedUUIDGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(TimeBasedUUIDGenerator.class);
public static final Object LOCK = new Object();
private static long lastTime;
private static long clockSequence = 0;
private static final long HOST_IDENTIFIER = getHostId();
private TimeBasedUUIDGenerator() {
// no-op
}
/**
* Will generate unique time based UUID where the next UUID is
* always greater then the previous.
*/
public static final UUID generateId() {
return generateIdFromTimestamp(System.currentTimeMillis(), 0L);
}
public static final UUID generateId(long hostId) {
return generateIdFromTimestamp(System.currentTimeMillis(), hostId);
}
public static final UUID generateIdFromTimestamp(long currentTimeMillis, long hostId) {
long time;
synchronized (LOCK) {
if (currentTimeMillis > lastTime) {
lastTime = currentTimeMillis;
clockSequence = 0;
} else {
++clockSequence;
}
}
// CHECKSTYLE:OFF
time = currentTimeMillis;
// low Time
time = currentTimeMillis << 32;
// mid Time
time |= ((currentTimeMillis & 0xFFFF00000000L) >> 16);
// hi Time
time |= 0x1000 | ((currentTimeMillis >> 48) & 0x0FFF);
long clockSequenceHi = clockSequence;
clockSequenceHi <<= 48;
long lsb = (hostId != 0L ? clockSequenceHi | hostId : clockSequenceHi | HOST_IDENTIFIER);
// CHECKSTYLE:ON
return new UUID(time, lsb);
}
private static final long getHostId() {
long macAddressAsLong = 0;
try {
Random random = new Random();
InetAddress address = InetAddress.getLocalHost();
NetworkInterface ni = NetworkInterface.getByInetAddress(address);
if (ni != null) {
byte[] mac = ni.getHardwareAddress();
random.nextBytes(mac); // we don't really want to reveal the actual MAC address
//Converts array of unsigned bytes to an long
if (mac != null) {
for (int i = 0; i < mac.length; i++) {
// CHECKSTYLE:OFF
macAddressAsLong <<= 8;
macAddressAsLong ^= (long) mac[i] & 0xFF;
// CHECKSTYLE:ON
}
}
}
} catch (Exception ex) {
LOGGER.error("getHostId Exception", ex);
}
LOGGER.debug("MAC Address (from Network Interface) : " + getMacAddressAsString(getMacAddress(macAddressAsLong)));
return macAddressAsLong;
}
public static byte[] getMacAddress(long address) {
// CHECKSTYLE:OFF
byte[] addressInBytes = new byte[] {
(byte)((address >> 40) & 0xff),
(byte)((address >> 32) & 0xff),
(byte)((address >> 24) & 0xff),
(byte)((address >> 16) & 0xff),
(byte)((address >> 8 ) & 0xff),
(byte)((address >> 0) & 0xff)
};
// CHECKSTYLE:ON
return addressInBytes;
}
public static String getMacAddressAsString(byte[] address) {
StringBuilder builder = new StringBuilder();
for (byte b : address) {
if (builder.length() > 0) {
builder.append(":");
}
// CHECKSTYLE:OFF
builder.append(String.format("%02X", b & 0xFF));
// CHECKSTYLE:ON
}
return builder.toString();
}
public static long getMacAddressAsLong(byte[] address) {
long mac = 0;
// CHECKSTYLE:OFF
for (int i = 0; i < 6; i++) {
long t = (address[i] & 0xffL) << ((5 - i) * 8);
mac |= t;
}
// CHECKSTYLE:ON
return mac;
}
}