[译]Codeuml-尽可能快地设计UML图
By robot-v1.0
本文链接 https://www.kyfws.com/applications/codeuml-design-uml-diagrams-as-fast-as-you-can-cod-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 11 分钟阅读 - 5136 个词 阅读量 0[译]Codeuml-尽可能快地设计UML图
原文地址:https://www.codeproject.com/Articles/397629/Codeuml-design-UML-diagrams-as-fast-as-you-can-cod
原文作者:Omar Al Zabir
译文由本站 robot-v1.0 翻译
前言
Codeuml.com is an open source free web based UML diagram editor. You can code uml diagrams as fast as you can type using a special language to describe the diagram.
Codeuml.com是一个免费的基于Web的开源Web UML图表编辑器.您可以使用特殊的语言来描述该图,从而以最快的速度编码uml图.
介绍(Introduction)
Codeuml是一个基于Web的UML设计器,您可以在其中使用特殊的语言对图表进行编码,并即时生成图表.它比使用任何视觉设计器都要快,在视觉设计器中,您必须拖放图表元素并使用鼠标进行连接. Codeuml使用开源(Codeuml is a web based UML designer where you code thediagram using a special language and it generates the diagram on the fly. It is faster than using any visual designer where you have to drag &drop diagram elements and use mouse to connect them. Codeuml uses the opensource) 植物(plantuml) 引擎到文本的生产图解.您可以以编写代码的最快速度生成UML图.(engine to producediagram from text. You can produce UML diagrams as fast as you can code.)
该Web应用程序显示了一些有趣的设计和编码挑战.首先,它向您展示了如何构建类似于Windows 8 Metro UI的基于Web的IDE类似环境.其次,它展示了如何定期从网站收集数据,如何异步在后台将其发送到服务器,以及如何实时获取生成的结果.第三,也是最重要的一点,它显示了如何维护非常昂贵的资源的服务器端池,您不能仅在每次服务器命中时就创建该池,并且必须具有由所有Web用户共享的有限池.(This web application shows some interesting design andcoding challenges. First, it shows you how to build a web based IDE likeenvironment that mimics Windows 8 Metro UI. Second it shows how you canperiodically collect data from the website, send to the server in thebackground asynchronously and get the result generated on the fly. Third andthe most important, it shows how you can maintain a server side pool of veryexpensive resource that you cannot just create on every hit to the server andmust have a finite pool that is shared by all your web users.)
获取代码(Get the code)
该实时站点位于:(The live site is available at:) www.codeuml.com(www.codeuml.com)
从以下位置获取代码:(Get the code from:) http://code.google.com/p/codeuml/(http://code.google.com/p/codeuml/)
建立前端(Building the front-end)
该用户界面的灵感来自Windows 8中的Metro UI,它是平板友好的.您可以轻松触摸平板电脑上的按钮. 3列可调整大小的面板使用(The UI is inspired by the metro UI in Windows 8 and it istablet friendly. You can easily touch the buttons on a tablet. The 3 columnresizable panels are built using) jQuerySplitter插件(jQuerySplitter plugin) .文本编辑器很棒(. The text editor is the awesome) 代码镜像(CodeMirror) 具有可怕徽标的文本编辑器.报价器由(text editor that has a horriblelogo. The ticker is provided by) jQuery新股票(jQueryNew Ticker) 插入.(plugin.)
3列视图使用以下html构建:(The 3 column view is built using the following html:)
<div id="MySplitter">
<div class="SplitterPane unselectable">
<div id="umlsnippets">
.
.
.
</div>
</div>
<div id="CenterAndRight">
<div class="SplitterPane">
<img src="img/ajax-loader.gif" id="ProgressIndicator" />
<textarea id="umltext" rows="10" cols="40"></textarea>
</div>
<div class="SplitterPane">
<div id="umlimage_container">
<img id="umlimage" src="img/defaultdiagram.png" />
<div id="ticker">
News ticker
</div>
</div>
</div>
</div>
</div>
首先,它将屏幕分为两部分-左侧的UML片段栏和具有编辑器和图像的右侧.然后将右侧分为两部分-文本编辑器和图表图像.以下JavaScript初始化拆分器:(First it divides the screen into 2 parts – the left side UML snippet bar and the right side that has the editor and the image. Then it divides the right side into further two parts – the text editor and the diagram image. The following javascript initializes the splitter:)
// Main vertical splitter, anchored to the browser window
$("#MySplitter").splitter({
type: "v",
outline: true,
minLeft: 60, sizeLeft: 100, maxLeft: 250,
anchorToWindow: true,
resizeOnWindow: true,
accessKey: "L"
});
// Second vertical splitter, nested in the right pane of the main one.
$("#CenterAndRight").splitter({
type: "v",
outline: true,
minRight: 200, sizeRight: ($(window).width() * 0.6), maxRight: ($(window).width() * 0.9),
accessKey: "R"
});
$(window).resize(function () {
$("#MySplitter").trigger("resize");
});
接下来,它会在文本区域上初始化CodeMirror代码编辑器,并使其成为出色的文本编辑器.(Next it initializes the CodeMirror code editor over thetextarea and makes it the awesome text editor.)
myCodeMirror = CodeMirror.fromTextArea($('#umltext').get(0),
{
onChange: refreshDiagram
});
myCodeMirror.focus();
myCodeMirror.setCursor({ line: myCodeMirror.lineCount() + 1, ch: 1 });
然后,它初始化左侧的UML片段栏.每个按钮都有一个关联的uml文本,在单击该文本时将其注入到文本编辑器中.按钮的示例:(Then it initializes the left side UML snippet bar. Eachbutton has an associated uml text which is injected to the text editor whenclicked. An example of a button:)
<div id="scrollable">
<!-- Sequence diagram -->
<h2>
Sequence
</h2>
<div class="sequence_diagram">
<div class="button">
<div class="icon">
A→B</div>
<div class="title">
Sync Msg</div>
<pre class="umlsnippet">A -> B: Sync Message</pre>
</div>
</div>
注入到文本编辑器中的文本位于(The text that is injected to the text editor is inside the) <pre>
标签.(tag.)
您可以根据需要创建任意数量的按钮,只需将需要插入的小片段(You can create as many buttons as you like and just put theuml snippet that needs to be inserted in the) <pre>
带有classumlsnippet的标签.(tag with classumlsnippet.)
单击此类按钮后,以下javascript会将代码注入其中(When such buttons are clicked, the following javascript injects the code inside) <pre>
进入文本编辑器.(into the text editor.)
$("#umlsnippets").find(".button").click(function () {
var diagramType = $(this).parent().attr("class");
if (lastUmlDiagram !== diagramType) {
if (!confirm("The current diagram will be cleared? Do you want to continue?"))
return;
myCodeMirror.setValue("");
}
changeDiagramType(diagramType);
var umlsnippet = $(this).find("pre.umlsnippet").text();
var pos = myCodeMirror.getCursor(true);
// When replaceRange or replaceSelection is called
// to insert text, in IE 8, the code editor gets
// screwed up. So, it needs to be recreated after this.
myCodeMirror.replaceRange(umlsnippet, myCodeMirror.getCursor(true));
// recreate the code editor to fix screw up in IE 7/8
myCodeMirror.toTextArea();
myCodeMirror = CodeMirror.fromTextArea($('#umltext').get(0),
{
onChange: refreshDiagram
});
myCodeMirror.focus();
myCodeMirror.setCursor(pos);
refreshDiagram();
});
这里一件棘手的事情是,如果我插入文字呼叫(One tricky thing here is that if I inject text calling) replaceRange
,CodeMirror编辑器停止工作.必须重新创建它才能使其正常工作.(,the CodeMirror editor stops working. It had to be recreated to make it workagain.)
键入时生成图(Generating diagram as you type)
键入时刷新图是最具挑战性的部分.一旦文本编辑器发生更改,就会触发以下javascript函数.但是,它确保仅每秒一次将UML发送到服务器.因此,即使您连续输入,它也只会每秒将UML发送到服务器一次.(Refreshing the diagram as you type is the most challengingpart. The following javascript function gets fired as soon as something changeson the text editor. However, it makes sure it sends the UML to the server onlyonce every second. So, even if you keep typing continuously, it will only sendthe UML to the server once per second.)
function refreshDiagram() {
if (lastTimer == null) {
lastTimer = window.setTimeout(function () {
// Remove starting and ending spaces
var umltext = myCodeMirror.getValue().replace(/(^[\s\xA0]+|[\s\xA0]+$)/g, '');
var umltextchanged =
(umltext !== lastUmlText)
&& validDiagramText(umltext);
if (umltextchanged) {
$('#ProgressIndicator').show();
lastUmlText = umltext;
$.post("SendUml.ashx", { uml: umltext }, function (result) {
var key = $.trim(result);
$("#umlimage").attr("src", "getimage.ashx?key=" + key);
}, "text");
try {
var forCookie = $.base64.encode(umltext).replace(/==/, '');
if (forCookie.length > 3800) {
alert("Sorry maximum 3800 characters allowed in a diagram");
}
else {
createCookie('uml', forCookie, 30);
var test = readCookie('uml');
if (test !== forCookie) {
createCookie('uml', '', 30);
}
}
} catch (e) {
}
}
}, 1000);
}
else {
window.clearTimeout(lastTimer);
lastTimer = null;
refreshDiagram();
}
}
该代码有很多智能.首先,要确保当用户仅输入空格或点击进入时,它不会将UML文本发送到服务器以进行昂贵的图像生成过程,并且文本中实际上没有任何变化,因此不会渲染新图像.它还进行了一些验证,以防止半烘焙图文本过早地发送到服务器.您可以捕获的内容越多,可以防止在服务器上生成的无用映像越少.(There’s quite some intelligence to this code. First itensures that it does not send the UML text to the server to go through anexpensive image generation process when user is only typing space or hittingenter and there’s really no change in the text that will result in a new imageto be rendered. It also does a little bit of validation to prevent half-bakeddiagram text from being prematurely sent to the server. The more you can catchhere, the less useless image generation you can prevent on the server.)
首先,它将UML文本发布到名为SendUml.ashx的HTTP处理程序中.它会记住文本并返回GUID.然后使用该GUID来(First it posts the UML text to an HTTP handler called SendUml.ashx.It remembers the text and returns a GUID. Then that GUID is used to hit the) GetImage.ashx
它负责生成图.中的代码(which takes care of generating the diagram. The code in) SendUml.ashx
很简单:(isvery simple:)
public class SendUml : IHttpHandler {
public void ProcessRequest (HttpContext context) {
string uml = context.Request["uml"];
string key = Guid.NewGuid().ToString();
context.Cache.Add(key, uml, null, DateTime.Now.AddSeconds(60), System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default, null);
context.Response.ContentType = "text/plain";
context.Response.Write(key);
}
它只是将文本存储在缓存中一小段时间,因为预期浏览器会点击(It just stores the text in cache for a short duration as itis expected that the browser will hit the) GetImage.ashx
获取密钥GUID之后立即进行.(immediatelyafter getting the key GUID.)
public void ProcessRequest (HttpContext context) {
string key = context.Request["key"];
string umltext = context.Cache[key] as string;
context.Response.ContentType = "image/png";
context.Response.Cache.SetCacheability(HttpCacheability.Private);
context.Response.Cache.SetExpires(DateTime.Now.AddMinutes(5));
if (context.Request["saveMode"] == "1")
{
context.Response.AddHeader("Content-Disposition", "attachment; filename=diagram.png");
}
var connection = PlantUmlConnectionPool.Get(TimeSpan.FromSeconds(15));
if (connection == null)
throw new ApplicationException("Connection not found in pool.");
try
{
var uploadFileName = key + ".txt";
var downloadFileName = key + ".png";
connection.Upload(uploadFileName,
"@startuml " + downloadFileName + Environment.NewLine +
umltext + Environment.NewLine +
"@enduml");
System.Threading.Thread.Sleep(100);
using (MemoryStream memoryStream = new MemoryStream())
{
connection.Download(downloadFileName, stream =>
{
byte[] buffer = new byte[0x1000];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, 0x1000)) > 0)
{
memoryStream.Write(buffer, 0, bytesRead);
}
});
首先,它从查询字符串中读取键,然后从缓存中加载UML文本.然后,它与PlantUml FTP服务器建立连接(在下一部分中即将进行解释),并将UML文本作为文件上传到FTP服务器.然后,Plantuml生成图表图像并使其可供下载.然后,处理程序从FTP服务器下载映像.然后,它将水印添加到图像并发送回浏览器.(First it reads the key from query string and then loads theUML text from cache. Then it gets a connection to PlantUml FTP server(explanation coming soon in next section) and uploads the UML text as a file tothe FTP server. Plantuml then generates the diagram image and makes itavailable for download. The handler then downloads the image from the FTPserver. It then adds a watermark to the image and sends back to the browser.)
using (Bitmap b = Bitmap.FromStream(memoryStream, true, false) as Bitmap)
using (Bitmap newBitmap = new Bitmap(b.Width, b.Height + 20))
using (Graphics g = Graphics.FromImage(newBitmap))
{
// Put the original image on the top left corner.
g.FillRectangle(Brushes.White, 0, 0, newBitmap.Width, newBitmap.Height);
g.DrawImage(b, 0, 0);
// Add the watermark
SizeF size = g.MeasureString(WatermarkText, _font);
g.DrawString(WatermarkText, _font, Brushes.Black, newBitmap.Width - size.Width, newBitmap.Height - 15);
// Save the image to the response stream directly.
newBitmap.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Png);
}
context.Response.Flush();
完成后,它将连接返回到池中:(Once done, it returns the connection back to the pool:)
PlantUmlConnectionPool.Put(connection);
从前端那边就是这样(That’s it from the front end side)
使用Plantuml生成图(Generating Diagram using Plantuml)
Plantuml是一个Java应用程序,可以作为FTP服务器运行,您可以在其中将图文本作为文件上传,并生成可以下载的图图像.由于它作为FTP服务器运行,因此我必须维护一个运行FTP服务器的池.我不能只启动FTP服务器然后生成映像.太慢了.因此,我必须在应用程序启动期间启动几个FTPserver实例,然后维护与FTP服务器的连接池.每当打击(Plantuml is a Java application that can run as a FTP serverwhere you can upload diagram text as a file and it generates a diagram imagethat you can download. Since it runs as a FTP server, I have to maintain a poolof running FTP servers. I cannot just start the FTP server and then generatethe image. It will be too slow. So, I have to launch couple of instances of FTPservers during application startup and then maintain a pool of connections toFTP server. Whenever a hit to) getimage.ashx
为了生成图表,它从池中获得一个连接,为请求提供服务,然后将连接返回到池中.当您必须在许多苛刻的客户中共享有限数量的昂贵资源时,可以使用这种常见模式.(comes in order to generate thediagram, it gets one connection from the pool, serves the request and thenreturns the connection to the pool. This is a common pattern you can use whenyou have to share a finite number of expensive resources across many demandingcustomers.)
首先,我维护一个正在运行的Plantuml实例池.(First I maintain a pool of running Plantuml instances.During) Application_Start
事件中,以下代码启动了几个PlantumlFTP服务器并准备了一个连接池.(event, the following code launches couple of PlantumlFTP servers and prepares a pool of connections.)
public static class PlantUmlProcessManager
{
private static readonly List<Process> _processes = new List<Process>();
public static void Startup()
{
if (_processes.Count > 0)
Shutdown();
var javaPath = ConfigurationManager.AppSettings["java"];
if (!File.Exists(javaPath))
throw new ApplicationException("Java.exe not found: " + javaPath);
var host = ConfigurationManager.AppSettings["plantuml.host"];
var startPort = Convert.ToInt32(ConfigurationManager.AppSettings["plantuml.start_port"]);
var instances = Convert.ToInt32(ConfigurationManager.AppSettings["plantuml.instances"]);
var plantumlPath = ConfigurationManager.AppSettings["plantuml.path"];
if (!File.Exists(plantumlPath))
throw new ApplicationException("plantuml.jar not found in " + plantumlPath);
for (int i = 0; i < instances; i++)
{
var argument = "-jar " + plantumlPath + " -ftp:" + (startPort + i);
ProcessStartInfo pInfo = new ProcessStartInfo(javaPath, argument);
pInfo.CreateNoWindow = true;
pInfo.UseShellExecute = false;
pInfo.RedirectStandardInput = true;
pInfo.RedirectStandardError = true;
pInfo.RedirectStandardOutput = true;
Process process = Process.Start(pInfo);
Thread.Sleep(5000);
_processes.Add(process);
PlantUmlConnection connection = new PlantUmlConnection();
connection.Connect(host, startPort + i);
PlantUmlConnectionPool.Put(connection);
}
}
连接池的定义如下:(The connection pool is defined as following:)
public static class PlantUmlConnectionPool
{
private readonly static Queue<PlantUmlConnection> _connectionPool = new Queue<PlantUmlConnection>();
private readonly static ManualResetEvent _availableEvent = new ManualResetEvent(false);
public static PlantUmlConnection Get(TimeSpan timeout)
{
if (_connectionPool.Count == 0)
{
_availableEvent.Reset();
if (_availableEvent.WaitOne(timeout))
{
return _connectionPool.Dequeue();
}
else
{
return null;
}
}
else
{
lock (_connectionPool)
{
if (_connectionPool.Count == 0)
return null;
else
return _connectionPool.Dequeue();
}
}
}
算法如下:(The algorithm is as following:)
- 检查池中是否有可用的免费连接.(Check if there’s a freeconnection available in the pool.)
- 如果没有,请等待一段固定的时间,直到某些连接可用.(If not, then wait for afixed duration until some connection becomes available.)
- 如果在等待超时时间后没有连接可用,则返回null.(If no connection isavailable after waiting the timeout period, then return null.) 将连接放回池中非常简单:(Putting connection back to the pool is very simple:)
public static void Put(PlantUmlConnection connection)
{
lock (_connectionPool)
_connectionPool.Enqueue(connection);
_availableEvent.Set();
}
为了维持与正在运行的FTP服务器的连接,我使用了(In order to maintain a connection ready to the running FTPservers, I have used) Alex Pilotti的FTP客户端(Alex Pilotti’s FTPclient) .(.)
public class PlantUmlConnection : IDisposable
{
private FTPSClient client = new FTPSClient();
private string _host;
private int _port;
public void Connect(string host, int port)
{
_host = host;
_port = port;
Debug.WriteLine("Connecting to FTP " + host + ":" + port);
client.Connect(host, port,
new NetworkCredential("yourUsername","yourPassword"),
ESSLSupportMode.ClearText,
null,
null,
0,
0,
0,
3000,
true,
EDataConnectionMode.Active
);
Debug.WriteLine("Connection successful " + host + ":" + port);
}
在FTP服务器初始化期间,对于FTP服务器的每个实例,此连接类的一个实例建立一个openconnection.(During the initialization of FTP servers, for each instanceof FTP server, one instance of this connection class establishes an openconnection.)
当需要生成图时,它将包含图文本的文本文件上载到FTP服务器.然后,Plantuml引擎启动并生成图像.(When a diagram needs to be generated, it uploads a text fileto the FTP server containing the diagram text. Then the Plantuml engine kicksin and generates the image.)
public void Upload(string remoteFileName, string content)
{
Debug.WriteLine("Uploading to " + _host + ":" + _port + "/" + remoteFileName);
using (var stream = client.PutFile(remoteFileName))
{
byte[] data = Encoding.UTF8.GetBytes(content);
stream.Write(data, 0, data.Length);
}
Debug.WriteLine("Successfully uploaded " + _host + ":" + _port + "/" + remoteFileName);
}
然后,您可以使用下载功能下载图像:(Then you can download the image using Download function:)
public void Download(string remoteFileName, Action<Stream> processStream)
{
Debug.WriteLine("Downloading from " + _host + ":" + _port + "/" + remoteFileName);
using (var stream = client.GetFile(remoteFileName))
{
processStream(stream);
}
Debug.WriteLine("Successfully downloaded " + _host + ":" + _port + "/" + remoteFileName);
}
这就是管理PlantUML服务器的全部.(That’s all about managing PlantUML server.)
自行设置Codeuml(Setting up codeuml on your own)
您可以在自己的服务器上安装codeuml.在这种情况下,请仔细阅读自述文件.为了使Plantuml引擎正常工作,需要进行一些非常仔细的设置.为了方便起见,我将粘贴自述文件,但会继续检查最新代码并阅读我的文件.(You can install codeuml on your own server. In that case,please follow the readme file carefully. It requires some very careful settingin order to get Plantuml engine to work. I will paste the readme file for yourconvenience but do keep checking the latest code and read me file.)
There are several pre-requisits before you run this website.
1. Install Java
===============
Download and install latest Java. Make sure you know where
you are installing java. Usually it will be:
"c:\Program Files\Java\jre6\bin"
1. Configure Graphviz
=============================================================
First, you have to install graphviz.
http://www.graphviz.org/
Once you have installed, create a SYSTEM environment variable
called GRAPHVIZ_DOT which points to the dot.exe found in the
graphviz bin folder. Usually it is:
c:\Program Files\Graphviz2.26.3\bin\dot.exe
Once you have done so, start a new command line window and run
this:
set graphviz_dot
If this shows you:
GRAPHVIZ_DOT=c:\Program Files\Graphviz2.26.3\bin\dot.exe
Then it is ok.
2. Installing on IIS 7+
=============================================================
If you are hosting this on a Windows Server, there are various
steps you need to do:
* First create a new app pool.
* Create a new website or virtual directory that points to this
website.
* Give the app pool user (IIS AppPool\YourAppPoolName or NETWORK
SERVICE)
Read & Execute permission on the:
** Java folder. Eg. "c:\Program Files\Java\jre6\bin"
** Graphviz bin folder: Eg c:\Program Files\Graphviz2.26.3\bin
** Within this website:
plantuml folder.
3. Configuring web.config
==============================================================
You must fix the following entries before you can run:
<add key="java" value="c:\Program Files\Java\jre6\bin\java.exe" />
<add key="plantuml.path" value="C:\Dropbox\Dropbox\OSProjects\PlantUmlRunner\plantuml\plantuml.jar"/>
These are both absolute paths. No relative path allowed.
4. Running and testing the website
============================================================
Run the Manage.aspx.
It will take a while to start the page as it tries to launch java
and run the plantuml engine at the application_start event.
Once the site is up and running, click on Test button to test
a UML generation. If it works, you have configured everything
properly.
Disable the Manage.aspx on production.
结论(Conclusion)
作为Web应用程序的Codeuml很小,但它展示了如何构建像IDE这样的Visual Studio的高响应AJAX前端,并使用一些非常昂贵的有限资源池从服务器生成输出.它向您展示了如何实现昂贵的资源池靠你自己(Codeuml as a web application is small but it shows how tobuild highly responsive AJAX front-end that mimics Visual Studio like IDE andgenerates output form the server using some very expensive pool of finite resource.It shows you how you can implement a pool of expensive resource on your own.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
UML C# .NET .NET3.5 ASP.NET 新闻 翻译