/*
* Copyright 1997-2008 Day Management AG
* Barfuesserplatz 6, 4001 Basel, Switzerland
* All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Day Management AG, ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Day.
*/
/**
* The <code>CRX.utils.Util</code> library contains utility methods for CRXDE.
* @static
* @class CRX.utils.Util
*/
CRX.util.Util = function() {
var lastAuthHeader = null;
var connectionData = null;
var globalDialogs = {};
// private property
var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// private method for UTF-8 encoding
var _utf8_encode = function (string) {
string = string.replace(/\\r\\n/g,'\\n');
var utftext = '';
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
};
var fileMap = {};
var eventObject = new Ext.util.Observable();
eventObject.addEvents("reinit");
return {
on : function(eventName, fn, scope, o) {
eventObject.on(eventName, fn, scope, o);
},
/**
* Evaluates and returns a string.
* @param {String} param The string
* @return The evaluated object
* @type Object
*/
eval: function(json) {
try {
return eval("(" + json + ")");
} catch (e) {
return null;
}
},
/**
* Returns the anchor part of the URL.
* @static
* @param {String} url The URL
* @return {String} The anchor
*/
getAnchor: function(url) {
if (url.indexOf("#") != -1) {
return url.substring(url.indexOf("#") + 1);
}
},
getURLParams: function(url) {
url = url || window.location.search;
url = url.split("?");
if (url.length > 1) {
return Ext.urlDecode(url[1]);
}
return {};
},
/**
* Returns the value of the cookie with the given name.
* @static
* @param {String} name The name of the cookie
* @return {String} The value of the cookie
*/
getCookie: function(name) {
var cname = encodeURIComponent(name) + "=";
var dc = document.cookie;
if (dc.length > 0) {
var begin = dc.indexOf(cname);
if (begin != -1) {
begin += cname.length;
var end = dc.indexOf(";", begin);
if (end == -1) end = dc.length;
return decodeURIComponent(dc.substring(begin, end));
}
}
return null;
},
/**
* Sets the value of the cookie with the given name.
* @static
* @param {String} name The name of the cookie
* @param {String} value The value of the cookie
* @param {String} path (Optional) The server path the cookie applies to
* @param {int} days (Optional) The number of days the cookie will live,
* no value creates a session cookie, 0 removes
* the cookie
* @param {String} domain (Optional) The server domain
* @param {boolean} secure (Optional) <code>true</code> if the
* connection is secure, <code>false</code> otherwise
* @return {String} The value of the cookie
*/
setCookie: function(name, value, path, days, domain, secure) {
if (days && (typeof(days) != "number")) days = 7;
var date;
if (days > 0) {
date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
} else if (days == 0) {
date = new Date(1970, 0, 1);
}
document.cookie = encodeURIComponent(name) + "=" +
encodeURIComponent(value) + "; " +
(date ? "expires=" + date.toGMTString() + "; " : "") +
(domain ? "domain=" + domain + "; " : "") +
(path ? "path=" + path : "") +
(secure ? "; secure" : "");
return value;
},
// public method for encoding
base64Encode: function (input) {
var output = '';
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
},
reinit: function() {
Ext.Ajax.request({
url:CRX.Util.getContextPath() + "/de/init.jsp",
success:function(response, options) {
connectionData = CRX.Util.eval(response.responseText);
eventObject.fireEvent("reinit", this);
if (CRX.NodetypeRegistry) {
// todo: probably the wrong place, use event listener
// reload node types
CRX.NodetypeRegistry.reload(function() {
var tree = Ext.getCmp(CRX.ide.TREE_ID);
tree.setWorkspace(CRX.Util.getWorkspace());
});
}
}
});
},
/**
* Creates a new dialog or returns an existing dialog if there was already a dialog
* created with the same xtype before.
*
* @param {String/Object} config either an xtype string or a full dialog config object
* @param {Object} dialogs (optional) cache for dialogs; if omitted, a global cache will be used
**/
getDialog: function(config, dialogs) {
dialogs = dialogs || globalDialogs;
var type = typeof config === "string" ? config : config.xtype;
config = typeof config === "string" ? {xtype: type} : config;
if (!dialogs[type]) {
dialogs[type] = Ext.ComponentMgr.create(config);
}
return dialogs[type];
},
getLocalWorkspacePath: function(path) {
var localPath = path.substr(path.indexOf(CRX.ide.JCR_ROOT_NAME)
+ CRX.ide.JCR_ROOT_NAME.length);
if (localPath == "") {
localPath = "/"; // it the root node
}
return decodeURIComponent(localPath);
},
convertToTreePath: function(path) {
// encode path
var labels = path.split("/");
for (var i=0; i<labels.length; i++) {
labels[i] = encodeURIComponent(labels[i]);
}
path = labels.join("/");
return "/" + CRX.Util.getWorkspace() + "/" + CRX.ide.JCR_ROOT_NAME + path;
},
setWorkspaceFromPath: function(path) {
connectionData.workspace = path.substr(0, path.indexOf(CRX.ide.JCR_ROOT_NAME) - 1).substr(1);
},
setConnectionData: function(data) {
connectionData = CRX.Util.eval(data);
},
getWorkspace: function() {
return connectionData.workspace;
},
getWorkspaces: function() {
return connectionData.workspaces;
},
getLaunchpadContextPath: function() {
return connectionData.launchpadContextPath == "/" ? "" : connectionData.launchpadContextPath;
},
getSpiBaseUrl: function() {
return CRX.Util.getContextPath() + "/server";
},
getSpiRootUrl: function() {
return CRX.Util.getSpiBaseUrl()
+ "/" + CRX.Util.getWorkspace()
+ "/" + CRX.ide.JCR_ROOT_NAME;
},
getJcrVersion: function() {
return connectionData.jcrVersion;
},
getContextPath: function() {
return connectionData.contextPath;
},
getUserID: function() {
return connectionData.userID;
},
getFileExtension: function(name) {
name = name.replace(/^\s|\s$/g, ""); //trims string
if (/\.\w+$/.test(name)) {
if (name.match(/([^\/\\]+)\.(\w+)$/)) {
return RegExp.$2;
}
}
},
getDateAsISO8601: function(date) {
return date.format("Y-m-d") + "T" + date.format("H:i:s.uP");
},
isFormatISO8601: function(date) {
return Date.parseDate(date,"Y-m-dTH:i:s.uP") != undefined;
},
getErrorMsgFromXML: function(response) {
var msgs, xmlDoc = response.responseXML;
if (xmlDoc) {
if (Ext.isGecko) {
msgs = xmlDoc.getElementsByTagName("dcr:message");
return msgs[0].textContent;
} else {
msgs = xmlDoc.getElementsByTagName("message");
return msgs.item(0).textContent;
}
} else {
return "Unknown error (Error Code: " + response.status + ")";
}
},
openActionDialog: function() {
var dialogId;
var loadSelection;
if (this.baseAction) {
dialogId = this.baseAction.initialConfig.dialogId;
loadSelection = this.baseAction.initialConfig.loadSelection;
} else {
dialogId = this.initialConfig.dialogId;
loadSelection = this.initialConfig.loadSelection;
}
var tree = Ext.getCmp(CRX.ide.TREE_ID);
var node = tree.getSelectionModel().getSelectedNode();
if (node) {
var dlg = CRX.Util.getDialog(dialogId);
dlg.init(node);
if (Ext.isFunction(dlg.reset)) {
dlg.reset();
}
if (loadSelection) {
var cb = function() {
dlg.show();
};
if (!node.isLoaded()) {
node.reload(cb);
} else {
cb.call();
}
} else {
dlg.show();
}
}
},
validateNodeName: function(value) {
var valid = true;
var tree = Ext.getCmp(CRX.ide.TREE_ID);
var selection = tree.getSelectionModel().getSelectedNode();
if (selection) {
var node = selection.findChild("name", value);
if (node) {
if (CRX.State.isDeleted(node.getRealPath())) {
valid = "A locally deleted node with the same name was detected. "
+"Please save changes in order to create a new node with this name.";
} else {
valid = "A node with this name already exist";
}
}
}
return valid;
// TODO validate name against child node definitions
},
createNode: function(name, text, type, parent, loader, atts, data) {
var attr = loader.processAttributes({
name:encodeURIComponent(name), text:text,
primaryType:type
}, type);
var node = loader.createNode(attr);
if (node) {
parent.appendChild(node);
var propDefs = CRX.NodetypeRegistry.getPropertyDefinitions([
node.getPrimaryType()
]);
if (!atts) {
atts = {};
}
atts[CRX.util.JCR_PRIMARY_TYPE] = type;
atts[":" + CRX.util.JCR_PRIMARY_TYPE] = CRX.util.NAME;
var records = loader.createPropertyRecords(atts, propDefs);
records.each(function(record) {
if (record.get("name") == CRX.util.JCR_DATA) {
record.set("type", CRX.util.BINARY);
if (data) {
record.set("value", data);
}
}
record.markDirty(); // ugly hack to deal with property grid
});
node.addPropertyRecords(records);
CRX.State.addTransientNode(node);
}
return node;
},
/**
* Recursive variant of Ext.applyIf(). The optional last param "r" is
* true/false (go recursive), or number (number of levels to go recursive);
* defaults to "true".
**/
applyDefaults: function(o, c, r) {
// default r to true
r = (typeof r == "undefined") ? true : r;
if (o && c) {
for (var p in c) {
if (typeof o[p] == "undefined") {
o[p] = c[p];
} else if (typeof o[p] == "object" && r) {
if (typeof r == "number") {
r--;
}
CRX.Util.applyDefaults(o[p], c[p], r);
}
}
}
return o;
},
/**
* Requests the specified URL from the server using GET. The request
* will be synchronous, unless a callback function is specified.
* @static
* @param {String} url The URL to request
* @param {Function} callback (optional) The callback function which is
* called regardless of success or failure and is passed the following
* parameters:<ul>
* <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
* <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
* <li><b>response</b> : Object<div class="sub-desc">The response object.</div></li>
* </ul>
* @return {Mixed} The response object or, if the
* request is asynchronous, the transaction ID
*/
httpGet: function(url, callback) {
if (callback != undefined) {
return Ext.Ajax.request({
url: url,
callback: callback
});
} else {
var request = document.all ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
try {
request.open("GET", url, false);
request.send(null);
return {
status: request.status,
body: request.responseText
};
} catch (e) {
return null;
}
}
},
/**
* Pre-caches file contents for {@link #loadFile()}.
*
* @param {String} path URL path to the file (without CRX.Util.getContentPath())
* @param {String} content file contents to cache
*/
storeFile: function(path, content) {
fileMap[path] = content;
},
/**
* Loads a server side file via Ajax and caches its contents internally.
* To pre-populate the cache, for example in a single production js file,
* {@link #storeFile()} can be used.
*
* @param {String} path URL path to the file (without CRX.Util.getContentPath(),
* but with leading slash)
* @param {Boolean} noCaching (optional) if true, the file will be fetched and not
* cached; if it was pre-cached via {@link #storeFile()}
* it will be removed from the cache. Useful for larger
* files that are used one time only
*
* @return {String} the file contents or null if not found
*/
loadFile: function(path, noCaching) {
if (fileMap[path] === undefined) {
var response = CRX.Util.httpGet( CRX.Util.getContextPath() + path );
if (response !== null && response.status == 200) {
if (noCaching) {
return response.body;
}
fileMap[path] = response.body;
return fileMap[path];
}
return null;
} else {
var content = fileMap[path];
if (noCaching) {
delete fileMap[path];
}
return content;
}
},
logout: function() {
Ext.Ajax.request({
url:CRX.Util.getContextPath() + "/de/logout.jsp",
params: {
},
success:function(response, options) {
CRX.Util.setAuthHeader("");
CRX.Util.reinit();
},
failure:function(response, options) {
}
});
return false;
},
isLoggedIn: function() {
return connectionData && connectionData.userID && connectionData.userID != "anonymous";
},
getAuthHeader: function() {
if (lastAuthHeader) {
return lastAuthHeader;
} else {
if (window.name.indexOf("Basic ") == 0) {
return window.name;
} else {
return null;
}
}
},
setAuthHeader: function(hdr) {
window.name = lastAuthHeader = hdr;
},
login: function(username, password, workspace, opts) {
Ext.Ajax.request({
url:CRX.Util.getContextPath() + "/de/login.jsp",
params: {
UserId: username,
Password: password,
Workspace: workspace,
_charset_: "utf-8",
".token": ""
},
success:function(response, options) {
// TODO: check response if token-auth is supported.
var tokenLoginSupported = !!CRX.Util.getCookie("login-token");
if (!tokenLoginSupported) {
// store auth header
CRX.Util.setAuthHeader("Basic " + CRX.Util.base64Encode(username + ":" + password));
CRX.Util.setCookie("login-workspace", workspace, CRX.Util.getContextPath());
}
if (opts.success) {
opts.success(response, options);
}
CRX.Util.reinit();
},
failure:function(response, options) {
if (opts.failure) {
opts.failure(response, options);
}
}
});
return false;
},
/**
* Utility for updating a panel body's contents regardless whether the panel
* is already rendered (ie. the body el is present) or not (ie. if called
* right after the constructor).
*/
updatePanel: function(panel, html) {
if (panel.body) {
panel.body.update(html);
} else {
panel.html = html;
}
},
/**
* Rendering bug utility that will re-set the current height of the
* Ext element or component passed.
*/
updateHeight: function(el) {
var h = el.getHeight();
el.setHeight(h + 1);
el.setHeight(h);
},
/**
* Rendering bug utility that will re-set the current width of the
* Ext element or component passed.
*/
updateWidth: function(el) {
var w = el.getWidth();
el.setWidth(w + 1);
el.setWidth(w);
}
};
}();
분들이 자바 웹 프로젝트를 진행할때 이클립스 IDE를 사용합니다. 물론 저도 그렇구요. 이클립스는 WTP라는 아주 훌륭한 플랫폼이 있어서 웹개발시에 작업하고 서버에 deploy하지 않고도 이클립스상에서 서버를 껐다 키고, 서버의 임시 deploy 디렉토리에 배포하여 빠르게 개발할수 있습니다.
하지만 Maven을 통해 프로젝트를 생성하면 바로 WTP를 사용할 수가 없습니다. 인터넷에 검색해보면 여러가지 적용방법이 있는데 그중 가장 간단한 방법인 이클립스의 m2eclipse plugin과 m2eclipse Extras plugin을 통해서 간단하게 WTP용 Maven 프로젝트를 생성해 보겠습니다.
전체적으로 3단계로 나눌수 있겠는데요. 첫째. 이클립스 플러그인인 m2eclipse 를 설치한다. (maven이 설치 되었다는 가정하에 진행하겠습니다.) 둘째. 이클립스 플러그인인 m2eclipse Extras 를 설치한다. 셋째. maven 프로젝트를 생성한다.
위와 같이 Maven Integration for Eclipse Extras와 Maven Integration for Eclipse WTP를 선택하시고 설치하시면 됩니다.
위의 4개를 보두 선택하셔도 상관없지만 좀더 빠른 설치를 위해서 필요한것만 설치했습니다.
3. Maven Project를 생성합니다. File >> New >> Other >> Maven >> Maven Project 를 선택합니다.
Use default Workspace location 을 선택하고 Next를 눌러줍니다.
다음 창에서 artifact Id가 maven-archetype-webapp를 선택합니다.
Group Id : 이 프로젝트를 구별하기 위한 일종의 ID입니다. 보통 package 와 똑같이 지정합니다. Artifact Id : Project 이름입니다. Package : 프로젝트에서 사용할 package이름입니다.
위와 같이 프로젝트를 생성하면 Dynamic Web Project가 생성됩니다.
지금부터 약간의 설정을 해야합니다.
새로 만들어진 프로젝트를 선택한후 오른쪽 마우스 클릭 >> Properties >> Project Facets를 선택합니다.
이곳에서 바꾸어야 할것은 3가지 입니다.
1. Dynamic Web Module의 버전 2. Dynamic Web Project의 Webroot 위치 3. Java 버전 변경 입니다.
먼저 첫번째를 수정하려면 다음과 같이 진행해야 합니다. Dynamic Web Module의 체크를 풀어준다 >> Apply 버튼을 누른다 >> Dynamic Web Module을 Version을 알맞게 변경한다.(저는 참고로 2.5로 변경하겠습니다.) >> Dynamic WebModule의 체크를 다시 한다
두번째 webroot의 위치를 변경하겠습니다. 위사진의 (3번)을 선택한다. >> 다음창에서 Context root는 프로젝트 이름으로(기본적으로 프로젝트 이름이고 바꾸고 싶은것으로 바꾸어도 됩니다. 하지만 전 default로 두겠습니다.) 설정하고 directory는 /src/main/webapp 로 세팅합니다.
이렇게 세팅하면 기존의 Dynamic Web Project의 webroot가 WebContent에서 /src/main/webapp로 변경됩니다.
세번째 Java Version을 변경합니다. 저는 1.6 버전을 사용할 것이기 때문에 1.6으로 선택하고 OK를 누르겠습니다.
이렇게 세팅하고 OK를 눌러 빠져나옵니다. 만약 위와같이 설정한뒤 프로젝트에 에러가 발생하면 아마 거의 대부분은 위에서 설정한 Java버전과 Build Path의 java버전과 맞지 않았을때 에러가 날것입니다. 당연히 Build Path의 자바 버전도 변경해 주면 에러는 없어집니다.
기본적인 설정은 여기 까지입니다. 하지만 메이븐을 많이 사용하신 분들이라면 뭔가 이상함을 느끼실 겁니다.
패키지 이름이 이상합니다..ㅜㅜ
보통 메이븐은 4가지 정도로 분리가 됩니다. 1) src/main/java --> back end를 구성할 java 파일을 작성합니다. 2) src/main/resources --> 프로젝트의 설정파일을 저장합니다. 3) src/test/java --> 테스트를 하기위한 테스트 케이스를 작성합니다. 4) src/test/resources --> 테스트를 하기위한 설정파일을 작성합니다.
이렇게 4가지가 있어야 하는데 지금은 src/main/resources 밖에 없습니다. 그렇다면 직접 만들어 주는 수밖에 없겠네요..
탐색기를 통해 해당 웹프로젝트 디렉토리로 이동합니다. 다음과 같은 디렉토리를 만듭니다.
1) src/main/java 2) src/test 생성후 /java /resources
이런 구조로 만들어 주시면 됩니다.
그리고 이클립스로 돌아오셔서 상단의 Navigator를 선택하시면 다음과 같은 구조를 보실수 있습니다. (Navigator가 없으시면 Window >> Show View >> Navigator 를 선택하시면 됩니다)
이중에 수정해야 할 파일은 2가지 입니다. .settings/org.eclipse.wst.common.component 와 .classpath 파일입니다.
먼저 .settings/org.eclipse.wst.common.component를 열고 다음을 추가합니다.
더보기
빨간 3줄을 추가해 줍니다.
다음으로 .classpath입니다.
더보기
위의 3줄을 추가해주고 그 아래 한줄을 수정해 줍니다.
이로서 maven + WTP의 통합 과정이 끝났습니다. 이제 불편하게 maven package를 만들어서 테스트 서버에 배포할 필요없이 바로 사용할 수 있습니다.